简体   繁体   English

为什么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()
{}

The error message: 错误消息:

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)]

However, if I change the class A as follows: 但是,如果我按如下方式更改A类:

class A final
{
public:
    A() = default;
    template<typename T, 
    bool = std::is_copy_constructible_v<
        remove_cvref_t<T>>> // ok
    A(T&&) {}
};

Then everything is ok. 一切都好。

Why does C++'s variable template not behave as expected? 为什么C ++的variable template表现不如预期?

At the point where std::is_copy_constructible_v<A> is being instantiated, ie immediately after the definition of isCc , A is not complete, while std::is_copy_constructible_v requires its template argument to be complete. 在实例化std::is_copy_constructible_v<A> ,即在isCc定义isCcA未完成,而std::is_copy_constructible_v要求其模板参数完成。

Whether this code should work is still a drafting issue: Core Language Issue 287 , so it is reasonable that some compilers accept your code while others reject it. 这段代码是否应该起作用仍然是一个起草问题: 核心语言问题287 ,所以一些编译器接受你的代码而其他编译器拒绝它是合理的。

In the version without isCc , even at the point std::is_copy_constructible_v<A> is being instantiated, A is complete 1 , so all compilers happily accept the code. 在没有isCc的版本中,即使在实例化std::is_copy_constructible_v<A>A也是完整的1 ,因此所有编译器都乐于接受代码。


1 Related rules in the standard: 1标准中的相关规则:

[class.member]/6 [class.member] / 6

A complete-class context of a class is a 一个完整的类的上下文是一个

  • function body, 功能体,
  • default argument, 默认参数,
  • noexcept-specifier ([except.spec]), noexcept-specifier([except.spec]),
  • contract condition, or 合同条件,或
  • default member initializer 默认成员初始化程序

within the member-specification of the class ... 在类的成员规范内......

[class.member]/7 [class.member] / 7

... The class is regarded as complete within its complete-class contexts ... ......在完整的课堂环境中,课程被认为是完整的......

I had successfully compiled the OP's original proposed code in Visual Studio 2017 CE version 15.8.6 with my compiler's language standard set to ISO C++ Latest Draft Standard (/std:c++latest) in my IDE's settings and my machine is running Windows 7 64bit Ultimate. 我已经在我的IDE's设置中将我的编译器语言标准设置为ISO C++ Latest Draft Standard (/std:c++latest) ,并且我的机器运行的是Windows 7,在Visual Studio 2017 CE版本15.8.6中成功编译了OP's原始建议代码64位终极版。 I built & rand the code in Debug - x86 mode. 我在Debug - x86模式下构建并修改了代码。

I even went as far and made a call to his function f() within main, and it still built, compiled, ran and exited without error. 我甚至走了很远,并在main中调用了他的函数f() ,它仍然构建,编译,运行和退出而没有错误。

He then replied back in the comments with: 然后,他在评论中回复说:

My compiler is clang 7.0 on windows 我的编译器在Windows上是clang 7.0

I don't know if this is either a bug in Clang's compiler or if Clang just interprets it differently. 我不知道这是否是Clang's编译器中的错误,或者Clang只是以不同的方式解释它。

Maybe try compiling your original attempt with different compilers if you are able to, try GCC or a different version of Clang and see if you get different results. 如果你能尝试用不同的编译器编译原始尝试,可以试试GCC或不同版本的Clang ,看看你是否得到不同的结果。

This is something interesting that I believe needs further investigation to determine if it has to do with Clang's compiler specifically or not. 这是一个有趣的东西,我认为需要进一步调查,以确定它是否与Clang's编译器有关。

is_copy_constructible_v has been added in C++17 while std::remove_cvref is to be added in C++20. is_copy_constructible_v已添加到C ++ 17中,而std::remove_cvref将添加到C ++ 20中。 C++20 support is still experimental. C ++ 20支持仍然是实验性的。

Writing proper C++14 code will solve the problem: 编写适当的C ++ 14代码将解决问题:

template<typename T>
constexpr bool isCc = std::is_copy_constructible<std::remove_reference_t<::std::remove_cv_t<T>>>::value;

or C++17: 或者C ++ 17:

template<typename T>
constexpr bool isCc = std::is_copy_constructible_v<std::remove_reference_t<::std::remove_cv_t<T>>>;

Answer to next question: 回答下一个问题:

std::is_copy_constructible template parameter requirements are std::is_copy_constructible模板参数要求是

T shall be a complete type, cv void, or an array of unknown bound. T应为完整类型,cv void或未知边界数组。

which is not in case of template<typename T, bool = isCc<T>> when T is A 这不是template<typename T, bool = isCc<T>>当T是Atemplate<typename T, bool = isCc<T>>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM