为什么 C++ 的“变量模板”没有按预期运行?
Why does C++'s `variable template` not behave as expected?
#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 Studio17\Community\VC\Tools\MSVC.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)]
但是,如果我将 class A
更改如下:
class A final
{
public:
A() = default;
template<typename T,
bool = std::is_copy_constructible_v<
remove_cvref_t<T>>> // ok
A(T&&) {}
};
那一切就ok了
为什么 C++ 的 variable template
没有按预期运行?
is_copy_constructible_v
已在 C++17 中添加,而 std::remove_cvref
将在 C++20 中添加。 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 shall be a complete type, cv void, or an array of unknown bound.
当 T 为 A
时,template<typename T, bool = isCc<T>>
不是
我在 Visual Studio 2017 CE 版本 15.8.6 中成功编译了 OP's
原始提议代码,我的编译器语言标准在我的 IDE's
设置中设置为 ISO C++ Latest Draft Standard (/std:c++latest)
我的机器是 运行 Windows 7 64bit Ultimate。我在 Debug - x86
模式下构建了 & 运行d 代码。
我什至走得更远,在 main 中调用了他的函数 f()
,它仍然构建、编译、运行 并没有错误地退出。
然后他在评论中回复:
My compiler is clang 7.0 on windows
我不知道这是 Clang's
编译器中的错误,还是 Clang
只是对它的解释不同。
如果可以的话,也许可以尝试使用不同的编译器编译您最初的尝试,尝试 GCC
或不同版本的 Clang
,看看是否会得到不同的结果。
这很有趣,我认为需要进一步调查以确定它是否与 Clang's
编译器有关。
在 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 标准中的相关规则:
A complete-class context of a class is a
- function body,
- default argument,
- noexcept-specifier ([except.spec]),
- contract condition, or
- default member initializer
within the member-specification of the class ...
... The class is regarded as complete within its complete-class contexts ...
#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 Studio17\Community\VC\Tools\MSVC.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)]
但是,如果我将 class A
更改如下:
class A final
{
public:
A() = default;
template<typename T,
bool = std::is_copy_constructible_v<
remove_cvref_t<T>>> // ok
A(T&&) {}
};
那一切就ok了
为什么 C++ 的 variable template
没有按预期运行?
is_copy_constructible_v
已在 C++17 中添加,而 std::remove_cvref
将在 C++20 中添加。 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 shall be a complete type, cv void, or an array of unknown bound.
当 T 为 A
template<typename T, bool = isCc<T>>
不是
我在 Visual Studio 2017 CE 版本 15.8.6 中成功编译了 OP's
原始提议代码,我的编译器语言标准在我的 IDE's
设置中设置为 ISO C++ Latest Draft Standard (/std:c++latest)
我的机器是 运行 Windows 7 64bit Ultimate。我在 Debug - x86
模式下构建了 & 运行d 代码。
我什至走得更远,在 main 中调用了他的函数 f()
,它仍然构建、编译、运行 并没有错误地退出。
然后他在评论中回复:
My compiler is clang 7.0 on windows
我不知道这是 Clang's
编译器中的错误,还是 Clang
只是对它的解释不同。
如果可以的话,也许可以尝试使用不同的编译器编译您最初的尝试,尝试 GCC
或不同版本的 Clang
,看看是否会得到不同的结果。
这很有趣,我认为需要进一步调查以确定它是否与 Clang's
编译器有关。
在 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 标准中的相关规则:
A complete-class context of a class is a
- function body,
- default argument,
- noexcept-specifier ([except.spec]),
- contract condition, or
- default member initializer
within the member-specification of the class ...
... The class is regarded as complete within its complete-class contexts ...