TL; DR:
为什么模板化函数不能访问与非模板化函数相同的转换?
struct A {
A(std::nullptr_t) {}
};
template <typename T>
A makeA(T&& arg) {
return A(std::forward<T>(arg));
}
void foo() {
A a1(nullptr); //This works, of course
A a2(0); //This works
A a3 = makeA(0); //This does not
}
背景
我试图编写一些模板化的包装类来使用现有类型,其目标是直接替换,只需要重写使用现在包装的值的现有代码。
我无法理解的一个特殊情况如下:我们有一个可以从std::nullptr_t
(这里称为A)构建的类,因此,有很多代码库中某些人为实例分配零的地方。
但是,尽管转发了构造函数,但不能为包装器分配零。我做了一个非常相似的例子,在不使用实际的包装类的情况下重现了这个问题 - 一个简单的模板化函数足以显示问题。
我想允许继续允许分配零的语法 - 它不是我最喜欢的,但是最小化摩擦以转移到更新的代码通常是让人们加入的必要条件使用它们。
我也不想添加一个构造函数,该构造函数接受除零以外的任何int,因为它非常荒谬,以前从未被允许过,并且它应该继续在编译时捕获。
如果这样的事情不可能,我会找到一个解释,因为到目前为止,据我所知,对我来说没有任何意义。
这个例子在VC ++中具有相同的行为(尽管智能感知它可以......),Clang和GCC。理想情况下,解决方案也适用于所有3个(4个智能感知)编译器。
更直接适用的例子如下:
struct A {
A(){}
A(std::nullptr_t) {}
};
template <typename T>
struct Wrapper {
A a;
Wrapper(const A& a):a (a) {}
template <typename T>
Wrapper(T&& t): a(std::forward<T>(t)){}
Wrapper(){}
};
void foo2() {
A a1;
a1 = 0; // This works
Wrapper<A> a2;
a2 = 0; //This does not
}
答案 0 :(得分:5)
为什么编译器决定将零视为int?
因为它是一个整数。
文字0是文字。文字可以做有趣的事情。字符串文字可以转换为const char*
或const char[N]
,其中N
是字符串+ NUL终止符的长度。文字0也可以做有趣的事情;它可用于初始化具有NULL指针常量的指针。它可用于初始化nullptr_t
类型的对象。当然,它可以用来创建一个整数。
但是一旦它作为参数传递,它就不再是一个神奇的编译器构造了。它成为具有具体类型的实际C ++对象。当涉及到模板参数推导时,它会得到最明显的类型:int
。
一旦成为int
,就会停止为文字0,其行为与其他任何int
完全相同。除非它在constexpr
上下文中使用(例如你的int(0)
),编译器可以确定它确实是文字0,因此可以采用它的神奇属性。函数参数永远不会constexpr
,因此它们无法参与其中。
答案 1 :(得分:1)
见[conv.ptr] / 1:
空指针常量是值为零的整数文字或类型为
Install-Package Microsoft.CrmSdk.CoreAssemblies
的prvalue。空指针常量可以转换为指针类型;结果是该类型的空指针值[...]
因此整数文字std::nullptr_t
可以转换为空指针。但是,如果您尝试将某个其他整数值(不是文字)转换为指针类型,则上述引用不适用。事实上,没有其他从整数到指针的隐式转换(因为[conv.ptr]中没有列出这样的转换),所以你的代码失败了。
注意:[expr.reinterpret.cast] / 5涵盖显式转换。