【问题标题】:Work around incomplete type in static assert解决静态断言中的不完整类型
【发布时间】:2017-03-14 06:28:46
【问题描述】:

当表达式依赖于类类型本身时,有没有办法在类中进行静态断言?也许延迟评估直到类型完成或模板实例化之后?

示例代码:

#include <type_traits>

template<typename T>
struct Test {
   T x = 0; // make non-trivial
   static_assert(std::is_trivial<Test<T>>::value, "");
};

int main() {
    // would like static assert failure, instead get 'incomplete type' error
    Test<int> test1;
    Test<float> test2;
    return 0;
}

【问题讨论】:

  • 在这种特定情况下,您可以将逻辑放入 TestImpl 并让 Test 继承它(使用继承的构造函数和 co。)并让其中的 static_assertTestImpl 上工作.但这当然不是一个通用的解决方案。
  • 听起来不错,但为什么你说这不是一个通用的解决方案?
  • 嗯,我在考虑一些无法正常工作的极端情况,但我再也看不到它们了。我会发布它作为答案。

标签: c++ templates typetraits static-assert


【解决方案1】:

这是一个使用辅助类和间接类型别名的解决方案。我相信这没有缺点。

template<typename T>
struct TestImpl {
    T x = 0; // make non-trivial
};

template<typename T>
struct TestHelper {
    using type = TestImpl<T>;
    static_assert(std::is_trivial<type>::value, "");
};

template<typename T>
using Test = typename TestHelper<T>::type;

编辑: 或者,可以将 TestHelper 移至 TestImpl:

template<typename T>
struct TestImpl {
    T x = 0; // make non-trivial

    struct Helper {
        using type = TestImpl;
        static_assert(std::is_trivial<type>::value, "");
    };
};

template<typename T>
using Test = typename TestImpl<T>::Helper::type;

【讨论】:

    【解决方案2】:

    我也在寻找带有 static_assert 的解决方案,但约束也有效:

    #include <type_traits>
    
    namespace Private {
    template<typename T>
    struct Test {
       T x = 0;
    };
    }
    
    template<typename T>
    requires std::is_trivial<Private::Test<T>>::value
    using Test = Private::Test<T>;
    
    int main() {
        Test<int> test1;
        Test<float> test2;
        return 0;
    }
    

    【讨论】:

      【解决方案3】:

      您可以定义一个析构函数并将 static_assert 放在那里,至少使用 g++ 5.4 -std=c++11 可以。

      【讨论】:

      • 它没有,因为它不再微不足道,即使你不初始化x
      • 正确,对于 std::trivialstd::is_trivially_copyable 这是正确的,对于其他用例它应该仍然适用。跨度>
      【解决方案4】:

      (远离cmets)

      您可以添加一个中间类来保留逻辑,并让您的客户实例化一个派生类,它只包含static_asserts

      #include <type_traits>
      
      template<typename T>
      struct TestImpl {
          T x = 0; // make non-trivial
      };
      
      template<typename T>
      struct Test : TestImpl<T> {
          static_assert(std::is_trivial<TestImpl<T>>::value, "");
          using TestImpl<T>::TestImpl; // inherit constructors
      };
      

      【讨论】:

      • 已修复。它有一个错字。这是using TestImpl&lt;T&gt;::TestImpl;,而不是using TestImpl::TestImpl;。对吗?
      • Tnx。你能评论我发布的解决方案吗?
      【解决方案5】:

      我不知道这是否完全合规,但gcc trunk and clang trunk accept this

      #include <type_traits>
      
      template<typename T>
      constexpr bool check_trivial()
      {
          static_assert(std::is_trivial_v<T>);
          return std::is_trivial_v<T>;
      }
      
      template<typename T>
      struct Test
      {
         Test() noexcept(check_trivial<Test<T>>()) = default;
      
         T x;
      };
      
      struct nontrivial
      {
          nontrivial(){}
      };
      
      int main() {
          // would like static assert failure, instead get 'incomplete type' error
          Test<int> test1;
          Test<nontrivial> test2;
          return 0;
      }
      

      使用 dtor 代替 ctor 时不起作用。 YMMV。

      这使您的类型成为非聚合类型。


      如果您的类型有任何强制成员函数,您可以将static_assert 放在其中一个函数体中。成员函数体在类定义结束后进行评估,因此看不到不完整的类型。

      对于模板,问题在于并非所有成员函数都总是被实例化。非实例化成员函数中的 static_assert 不会触发。您需要在代码中的某处调用(实际上是任何 ODR 使用)来强制实例化。

      【讨论】:

        猜你喜欢
        • 2014-11-05
        • 1970-01-01
        • 2011-12-25
        • 2015-08-09
        • 1970-01-01
        • 2016-01-18
        • 1970-01-01
        • 1970-01-01
        • 2013-04-30
        相关资源
        最近更新 更多