为什么C ++的“变量模板”表现不如预期?

时间:2018-12-16 09:34:01

标签: c++ templates c++17 template-meta-programming typetraits

#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的行为不符合预期?

3 个答案:

答案 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 标准中的相关规则:

[class.member]/6

  

类的完整类上下文

     
      
  • 功能主体
  •   
  • 默认参数
  •   
  • noexcept-specifier([except.spec]),
  •   
  • 合同条件,或
  •   
  • 默认成员初始化程序
  •   
     

在类的成员规范中...

[class.member]/7

  

...该类在其完整类上下文中被认为是完整的...

答案 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只是以不同的方式解释它。

如果可以的话,也许尝试使用其他编译器编译原始尝试,尝试GCCClang的其他版本,看看是否得到不同的结果。

我认为这很有趣,需要进一步研究以确定它是否与Clang's编译器有关。

答案 2 :(得分:1)

在C ++ 17中添加了

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则不是这种情况