为什么这个用户定义的转换没有完成?

为什么这个用户定义的转换没有完成?

问题描述:

考虑:为什么这个用户定义的转换没有完成?

template<typename T> 
struct Prop 
{ 
    T value; 
    operator T() { return value; } 
}; 

int main() 
{ 
    Prop<float> p1 { 5 }; 
    Prop<std::vector<float>> p2 { { 1, 2, 3 } }; 

    float f1 = p1;    // Works fine 
    float f2_1_1 = p2.value[0]; // Works fine 
    float f2_1_2 = p2[0];  // Doesn't compile 

    return 0; 
} 

为什么不标明为行编译?它不应该使用提供的转换运算符执行隐式转换为std::vector<>,以便可以找到[]

在这个网站上有(许多)其他问题要求对这个问题的变化,但我找不到一个,我认为适用于此。它与std::vector是模板有关吗?

+2

表达式'p2 [0]'实际上与'p2.operator [](0)'相同(应该可以从错误消息中推断出来)。而'Prop'类没有'operator []'函数。 –

+0

所以问题是,p2 [0]被认为是一个表达式,对吧?因为如果p2被认为是一个表达式,转换为std :: vector将被执行,然后.operator [](0)将被调用,对吧?那么是否有一种便捷的方式让Prop以我想要的方式行事 - 即作为T型某些值的透明容器以及一些属性(因为在我的真实代码中,Prop有额外的成员)。 – Roel

+0

像这样包装数据的常用方法是重载成员访问操作符' - >'或解引用操作符'*'。然后你可以在(0)'或者'(* p2)[0]''上做'p2->。它比较麻烦,但仍比'static_cast'好。 –

隐式转换不被认为是对的成员函数调用的对象,包括下标操作过载。

考虑后果如果允许的话:每次任何未声明的成员函数都是这样调用的,编译器必须找出该对象可以转换的所有类型(注意,任何其他不相关的类型都可以有一个转换构造函数),并检查是否声明了缺少的成员函数。更不用说这对代码读者会有多混淆(转换可能在转换运算符的情况下显而易见,但不在转换构造函数的情况下,并且据我所知,它们的处理方式不同)。

那么,有没有notationally方便的方式来获得道具的行为我想要的方式

你必须定义一个成员函数为每个你想传递通矢量的成员函数通过透明。以下标操作符为例:

auto operator[](std::size_t pos) { 
    return value[pos]; 
} 
auto operator[](std::size_t pos) const { 
    return value[pos]; 
} 

问题当然是所有包装的成员函数都必须显式声明。另一个问题是类型取决于T的论点。例如,vector::operator[]使用vector::size_type,可能不会为您可能使用的所有T定义(当然不适用于float)。在这里,我们做出妥协并使用std::size_t

创建这种“透明”包装的一种较不费力的方式是继承。一个公开继承的模板会自动拥有父代的所有成员函数,并且可以隐式转换为它,并且可以通过父类型的指针和引用来引用。但是,这种方法的透明度有点问题,主要是因为~vector不是虚拟的。

私有继承允许同一包装为您的会员的方法,但用更漂亮的语法:

template<typename T> 
struct Prop : private T 
{ 
    using T::operator[]; 
    using T::T; 
}; 

注意继承方法阻止您使用基本类型为T。此外,它使隐式转换不可能(即使使用转换运算符),因此您不能在T的免费函数中使用Prop作为T


PS。请注意,您的转换运算符会返回一个值,因此必须复制该向量,这可能不合意。考虑提供参考版本:

operator T&&()&&   { return *this; } 
operator T&()&    { return *this; } 
operator const T&() const& { return *this; } 
+0

这很有道理,谢谢。 – Roel

+0

非公有继承,并使用'using'发布接口的所需部分可能是继承问题的解决方案。 – Angew

+0

“这种方法的问题当然是,那么你不能只包装任何类型,只能包装那些提供包装函数的类型。”不完全的。由于包装是一个模板,除非被调用,否则函数将不会被实例化。所以你可以*包装不支持'[]'的类型,在这种情况下你不能将'[]'应用于包装器。 – Angew

同样的方式,任何

p2.size(); 
p2.begin(); 
p2.push_back(24); 
// etc. 

没有意义编译

p2[0]; 

相当于拥有了

p2.operator[](0); 

没有按”没有道理编辑\


如果你想要这种行为(即,对于Prop<T>借用T成员)Bjarne有一个C++提议将点运算符添加到语言中。我的工作方式与运营商->适用于智能指针相同。 AFAIR有很多争议,所以我不会屏住呼吸。

A bit of background for the operator dot proposal—Bjarne Stroustrup

Operator Dot (R3) - Bjarne Stroustrup, Gabriel Dos Rei

Smart References through Delegation: An Alternative to N4477's Operator Dot - Hubert Tong, Faisal Vali

Alternatives to operator dot - Bjarne Stroustrup