MSVC++:模板的 static_assert 不会在 lambda 内触发

Posted

技术标签:

【中文标题】MSVC++:模板的 static_assert 不会在 lambda 内触发【英文标题】:MSVC++: template's static_assert is not triggered inside a lambda 【发布时间】:2018-05-21 03:42:05 【问题描述】:

更新 2:

这已在 VS 2019 Preview 16.1 Preview 1 中得到修复。

更新:

我已在visualstudio.com 提交错误报告。


所以我开始研究 C++ 的模板,当我试图阻止使用 static_assert 编译模板类时遇到了这个问题。

基本上,static_assert 错误在 VS2017 上使用 C++ 语言标准:ISO C++17 标准 (/std:c++17) 位于 lambda 内时不会触发。

我也在 gcc-7 上使用 -std=c++17 进行了尝试,并触发了错误。这是 VS2017 上的错误还是我遗漏了什么?

代码示例:

#include <iostream>
#include <string>
#include <type_traits>


template<typename T, typename Enable = void>
class IntegralContainer

    static_assert(std::is_integral<T>::value, "Type must be an integral!");
;

template<typename T>
class IntegralContainer<T, typename std::enable_if< std::is_integral<T>::value >::type >

private:
    T _value;

public:
    IntegralContainer(T value)
        : _value(value)
    
    
;

int main()

    IntegralContainer<int> int_container(1);
    // static_assert message is shown here.
    // > error C2338: Type must be an integral!
    // IntegralContainer<std::string> str_container;

    []() 
        // static_assert is not triggered here.
        IntegralContainer<std::string> str_container;
    ();

    std::cout << "Hello World!\n";

    return 0;

【问题讨论】:

来自Godbolt,看起来它甚至在触发错误之前优化了lambda的内容(这将是一个错误)...... 但是如果你给主模板一个带有副作用的默认构造函数,这些副作用确实会发生。 【参考方案1】:

它正在优化永远无法执行的代码。 绝对是一个错误。 lambda 正在进行一些(可能是语义替换)基本优化。

生成的程序集显示没有为该行生成代码 IntegralContainer str_container

  []() 
00F72280  push        ebp  
00F72281  mov         ebp,esp  
00F72283  sub         esp,0D8h  
00F72289  push        ebx  
00F7228A  push        esi  
00F7228B  push        edi  
00F7228C  push        ecx  
00F7228D  lea         edi,[ebp-0D8h]  
00F72293  mov         ecx,36h  
00F72298  mov         eax,0CCCCCCCCh  
00F7229D  rep stos    dword ptr es:[edi]  
00F7229F  pop         ecx  
00F722A0  mov         dword ptr [this],ecx  
    // static_assert is not triggered here.
    IntegralContainer<std::string> str_container;
  ();
00F722A3  push        edx  
00F722A4  mov         ecx,ebp  
00F722A6  push        eax  
00F722A7  lea         edx,ds:[0F722BCh]  
00F722AD  call        @_RTC_CheckStackVars@8 (0F712B2h)  
00F722B2  pop         eax  
00F722B3  pop         edx  
00F722B4  pop         edi  
00F722B5  pop         esi  
00F722B6  pop         ebx  
00F722B7  mov         esp,ebp  
00F722B9  pop         ebp  
00F722BA  ret  

如果静态断言放在公共 ctor 中。

#include <iostream>
#include <string>
#include <type_traits>


template<typename T, typename Enable = void>
class IntegralContainer

public:
  IntegralContainer(T const& value) 
    static_assert(std::is_integral<T>::value, "Type must be an integral!");
  
;

template<typename T>
class IntegralContainer<T, typename std::enable_if< std::is_integral<T>::value >::type >

private:
  T _value;

public:
  IntegralContainer(T value)
    : _value(value)
  
  
;

int main()

  IntegralContainer<int> int_container(1);
  // static_assert message is shown here.
  // > error C2338: Type must be an integral!
  //IntegralContainer<std::string> str_container;

  []() 
    // static_assert is not triggered here.
    IntegralContainer<std::string> str_container(std::string(""));
  ();

  std::cout << "Hello World!\n";

  return 0;

static_assert 被触发。

【讨论】:

AFAICT 预处理器在这方面也无可指责。

以上是关于MSVC++:模板的 static_assert 不会在 lambda 内触发的主要内容,如果未能解决你的问题,请参考以下文章

MSVC 在不知道类型的情况下评估上下文(和错误)

C++11 static_assert关键字

C++11 static_assert关键字

如何用宏做static_assert?

C ++编译时检查模板类型中是否存在方法

MSVC:隐式模板实例化,但未使用模板构造函数