考虑:
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
作为模板有关?
答案 0 :(得分:4)
对于成员函数调用的对象,不考虑隐式转换,包括下标运算符重载。
考虑允许的后果:每次调用任何未声明的成员函数时,编译器必须弄清楚对象可以转换为的所有类型(请注意,任何其他不相关的类型都可能具有转换构造函数),并检查是否已声明缺少的成员函数。更不用说对于代码的读者来说会有多么混乱(转换在转换运算符的情况下可能是显而易见的,但在转换构造函数的情况下并不明显,据我所知,它们不会以其他方式区别对待)。 / p>
因此,有一种符合标准的方式让Prop以我想要的方式行事
您必须为要传递的vector的每个成员函数定义一个成员函数,透明地传递。下面以下标运算符为例:
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
。此外,它使隐式转换成为不可能(即使使用转换运算符),因此您无法在期望Prop
的自由函数中将T
用作T
。
PS。请注意,您的转换运算符返回一个值,因此必须复制向量,这可能是不合需要的。请考虑提供参考版本:
operator T&&()&& { return *this; }
operator T&()& { return *this; }
operator const T&() const& { return *this; }
答案 1 :(得分:1)
与任何
相同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