C++ 为什么不从构造函数中推断模板参数?
我今天的问题很简单:为什么编译器不能从类构造函数推断模板参数,就像它可以从函数参数推断一样?例如,为什么以下代码无效:C++ 为什么不从构造函数中推断模板参数?,c++,templates,parameters,inference,C++,Templates,Parameters,Inference,我今天的问题很简单:为什么编译器不能从类构造函数推断模板参数,就像它可以从函数参数推断一样?例如,为什么以下代码无效: template <typename obj> class Variable { obj data; public: Variable(obj d) { data = d; } }; int main() { int num = 2; Variable var(num); // would be equivalent to Vari
template <typename obj>
class Variable {
obj data;
public:
Variable(obj d) { data = d; }
};
int main() {
int num = 2;
Variable var(num); // would be equivalent to Variable<int> var(num),
return 0; // but actually a compile error
}
模板
类变量{
obj数据;
公众:
变量(objd){data=d;}
};
int main(){
int num=2;
变量var(num);//将等效于变量var(num),
返回0;//但实际上是一个编译错误
}
正如我所说,我明白这是无效的,所以我的问题是为什么不是?允许这样做会造成任何重大的语法漏洞吗?是否存在不需要此功能的实例(推断类型会导致问题)?我只是想理解允许对函数进行模板推理背后的逻辑,但不允许对适当构造的类进行模板推理。你是对的,编译器可以很容易地猜到,但据我所知,它不在标准或C++0x中,因此你必须再等待至少10年(ISO标准固定周转率)在编译器提供程序添加此功能之前使ctor成为模板,变量只能有一种形式,但有多种ctor:
class Variable {
obj data; // let the compiler guess
public:
template<typename obj>
Variable(obj d)
{
data = d;
}
};
int main()
{
int num = 2;
Variable var(num); // Variable::data int?
float num2 = 2.0f;
Variable var2(num2); // Variable::data float?
return 0;
}
类变量{
obj data;//让编译器猜测
公众:
模板
变量(obj d)
{
数据=d;
}
};
int main()
{
int num=2;
变量var(num);//变量::数据int?
浮动num2=2.0f;
变量var2(num2);//变量::数据浮点?
返回0;
}
看到了吗?我们不能有多个Variable::data成员。请参阅以获取更多信息。我认为这是无效的,因为构造函数并不总是类的唯一入口点(我说的是copy constructor和operator=)
MyClass m(string s);
MyClass *pm;
*pm = m;
我不确定解析器是否会如此明显地知道MyClass pm的模板类型
不确定我说的是否有意义,但请随意添加一些评论,这是一个有趣的问题
C++ 17
可以接受的是,C++17将从构造函数参数中进行类型推断
示例:
std::pair p(2, 4.5);
std::tuple t(4, 3, 2.5);
许多类不依赖于构造函数参数。只有少数几个类只有一个构造函数,并基于该构造函数的类型进行参数化 如果确实需要模板推断,请使用帮助器函数:
template<typename obj>
class Variable
{
obj data;
public:
Variable(obj d)
: data(d)
{ }
};
template<typename obj>
inline Variable<obj> makeVariable(const obj& d)
{
return Variable<obj>(d);
}
模板
类变量
{
obj数据;
公众:
变量(obj d)
:数据(d)
{ }
};
模板
内联变量makeVariable(const obj&d)
{
返回变量(d);
}
假设编译器支持您的请求。那么这个代码是有效的:
Variable v1( 10); // Variable<int>
// Some code here
Variable v2( 20.4); // Variable<double>
编译器应该做什么?使用泛型变量类定义来推断它是变量,然后发现变量没有提供一个参数构造函数?让我们看看每个人都应该熟悉的类的问题-std::vector 首先,vector的一个非常常见的用法是使用不带参数的构造函数:
vector <int> v;
我们得到的是整数的向量,而不是字符串,大概它没有大小
最后,考虑构造多个参数的构造函数:“推断”:
应该使用哪个参数进行推断?我们需要某种方式告诉编译器它应该是第二个一类简单的向量,不难理解推理的原因。
类型的推导仅限于当前C++中的模板函数,但长期以来一直被认为在其他上下文中的类型推导是非常有用的。因此C++0x的
auto
虽然您的建议在C++0x中是不可能实现的,但以下内容表明您可以非常接近:
template <class X>
Variable<typename std::remove_reference<X>::type> MakeVariable(X&& x)
{
// remove reference required for the case that x is an lvalue
return Variable<typename std::remove_reference<X>::type>(std::forward(x));
}
void test()
{
auto v = MakeVariable(2); // v is of type Variable<int>
}
模板
变量MakeVariable(X&&X)
{
//删除x为左值时所需的引用
返回变量(std::forward(x));
}
无效测试()
{
auto v=MakeVariable(2);//v是变量类型
}
仍然缺失:这使得以下代码非常模糊:
int main()
{
int num = 2;
Variable var(num); // If equivalent to Variable<int> var(num),
Variable var2(var); //Variable<int> or Variable<Variable<int>> ?
}
intmain()
{
int num=2;
变量var(num);//如果等价于变量var(num),
变量var2(var);//变量还是变量?
}
由于其他人提出的原因,您不能按要求行事,但您可以这样做:
template<typename T>
class Variable {
public: Variable(T d) {}
};
template<typename T>
Variable<T> make_variable(T instance) {
return Variable<T>(instance);
}
C++03和C++11标准不允许从传递给constructor的参数中推断模板参数 但是有一个“构造函数模板参数推断”的建议,所以您可能很快就会得到您想要的编辑:事实上,C++17已经确认了此功能。
请参阅:在2016年的开明时代,自从有人提出这个问题以来,我们制定了两个新标准,而且一个新标准即将出台,关键是要知道支持C++17标准的编译器将 C++17中类模板的模板参数推导 (由Olzhas Zhumabek编辑的接受答案提供)是详细说明标准相关变更的文件 解决来自其他答案的担忧 当前最高评级的答案 这个答案指出,“复制构造函数和
操作符=
”不知道正确的模板专门化
这是胡说八道,因为标准的复制构造函数和运算符=
只存在于已知的模板类型中:
template <typename T>
class MyClass {
MyClass(const MyClass&) =default;
... etc...
};
// usage example modified from the answer
MyClass m(string("blah blah blah"));
MyClass *pm; // WHAT IS THIS?
*pm = m;
在这里,pm
已经是正确的类型,因此推断很简单。此外,调用复制构造函数时不可能意外地混合类型:
MyClass m(string("blah blah blah"));
auto pm = &(MyClass(m));
此处,pm
将是指向m
副本的指针。在这里,MyClass
是从m
复制而来的,它的类型是MyClass
(而不是不存在的类型MyClass
)。因此,在推断出pm的类型时,有足够的信息
template <class X>
Variable<typename std::remove_reference<X>::type> MakeVariable(X&& x)
{
// remove reference required for the case that x is an lvalue
return Variable<typename std::remove_reference<X>::type>(std::forward(x));
}
void test()
{
auto v = MakeVariable(2); // v is of type Variable<int>
}
int main()
{
int num = 2;
Variable var(num); // If equivalent to Variable<int> var(num),
Variable var2(var); //Variable<int> or Variable<Variable<int>> ?
}
template<typename T>
class Variable {
public: Variable(T d) {}
};
template<typename T>
Variable<T> make_variable(T instance) {
return Variable<T>(instance);
}
auto v = make_variable(instance);
template <typename T>
class MyClass {
MyClass(const MyClass&) =default;
... etc...
};
// usage example modified from the answer
MyClass m(string("blah blah blah"));
MyClass *pm; // WHAT IS THIS?
*pm = m;
MyClass m(string("blah blah blah"));
decltype(m) *pm; // uses type inference!
*pm = m;
MyClass m(string("blah blah blah"));
auto pm = &(MyClass(m));
MyClass s(string("blah blah blah"));
MyClass i(3);
i = s;
MyClass(const MyClass&);
template <typename T> foo();
foo();
Variable var(num); // If equivalent to Variable<int> var(num),
Variable var2(var); // Variable<int> or Variable<Variable<int>> ?
Variable var(num); // infering ctor
Variable var2(var); // copy ctor
Variable var3(move(var)); // move ctor
// Variable var4(Variable(num)); // compiler error