#include <type_traits>
template<typename T>
struct remove_cvref
{
using type = std::remove_cv_t<
std::remove_reference_t<T>>;
};
template<typename T>
using remove_cvref_t =
typename remove_cvref<T>::type;
template<typename T>
constexpr bool isCc = std::is_copy_constructible_v<
remove_cvref_t<T>>;
class A final
{
public:
A() = default;
template<typename T, bool = isCc<T>> // error
A(T&&) {}
};
A f()
{
A a;
return a;
}
int main()
{}
错误消息:
error : constexpr variable 'isCc<const A &>' must be initialized by a constant expression
1>main.cpp(14): note: in instantiation of variable template specialization 'isCc<const A &>' requested here
1>main.cpp(15): note: in instantiation of default argument for 'A<const A &>' required here
1>C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\type_traits(847): note: while substituting deduced template arguments into function template 'A' [with T = const A &, b1 = (no value)]
1>main.cpp(8): note: in instantiation of variable template specialization 'std::is_copy_constructible_v<A>' requested here
1>main.cpp(14): note: in instantiation of variable template specialization 'isCc<A>' requested here
1>main.cpp(15): note: in instantiation of default argument for 'A<A>' required here
1>main.cpp(21): note: while substituting deduced template arguments into function template 'A' [with T = A, b1 = (no value)]
但是,如果我如下更改类A
:
class A final
{
public:
A() = default;
template<typename T,
bool = std::is_copy_constructible_v<
remove_cvref_t<T>>> // ok
A(T&&) {}
};
那一切都很好。
为什么C ++的variable template
的行为不符合预期?
答案 0 :(得分:5)
在实例化std::is_copy_constructible_v<A>
的时刻,即紧随isCc
的定义之后,A
不完整,而std::is_copy_constructible_v
要求其模板参数完整。
此代码是否应该工作仍然是一个起草问题:Core Language Issue 287,因此某些编译器接受您的代码而其他人拒绝您的代码是合理的。
在没有isCc
的版本中,甚至在实例化std::is_copy_constructible_v<A>
时,A
都是完整的 1 ,因此所有编译器都乐于接受代码。
1 标准中的相关规则:
类的完整类上下文是
- 功能主体
- 默认参数
- noexcept-specifier([except.spec]),
- 合同条件,或
- 默认成员初始化程序
在类的成员规范中...
...该类在其完整类上下文中被认为是完整的...
答案 1 :(得分:2)
我已成功在Visual Studio 2017 CE版本15.8.6中编译了OP's
原始建议的代码,并且在ISO C++ Latest Draft Standard (/std:c++latest)
设置中将编译器的语言标准设置为IDE's
,并且计算机正在运行Windows 7 64位旗舰版。我以Debug - x86
模式构建和修改了代码。
我什至走了很远,在main中调用了他的函数f()
,它仍然在构建,编译,运行和退出时没有错误。
他随后在评论中回复:
我的编译器在Windows上是lang 7.0
我不知道这是Clang's
编译器中的错误还是Clang
只是以不同的方式解释它。
如果可以的话,也许尝试使用其他编译器编译原始尝试,尝试GCC
或Clang
的其他版本,看看是否得到不同的结果。
我认为这很有趣,需要进一步研究以确定它是否与Clang's
编译器有关。
答案 2 :(得分:1)
is_copy_constructible_v
,而在C ++ 20中添加了std::remove_cvref
。 C ++ 20支持仍处于试验阶段。
编写适当的C ++ 14代码将解决此问题:
template<typename T>
constexpr bool isCc = std::is_copy_constructible<std::remove_reference_t<::std::remove_cv_t<T>>>::value;
或C ++ 17:
template<typename T>
constexpr bool isCc = std::is_copy_constructible_v<std::remove_reference_t<::std::remove_cv_t<T>>>;
下一个问题的答案:
std::is_copy_constructible
模板参数要求是
T应该是完整类型,cv void或未知范围的数组。
当T为template<typename T, bool = isCc<T>>
时A
则不是这种情况