解决静态断言中不完整的类型
Work around incomplete type in static assert
当表达式依赖于 class 类型本身时,有没有办法在 class 中 static_assert?也许延迟评估直到类型完成或模板实例化之后?
示例代码:
#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;
}
您可以定义一个析构函数并将 static_assert 放在那里,至少对于 g++ 5.4 -std=c++11 这是有效的。
(从评论中移动)
您可以添加一个中间 class 来保持逻辑,并让您的客户实例化一个派生的,它只包含 static_assert
s
#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
};
这是一个使用助手 class 和间接类型别名的解决方案。我相信这没有缺点。
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;
我也在寻找 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;
}
我不知道这是否完全合规,但两者都 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
放在其中一个函数体中。成员函数体在 class 定义结束后计算,因此看不到不完整的类型。
对于模板,问题是并非所有成员函数都总是被实例化。非实例化成员函数中的 static_assert
不会触发。您需要在代码中的某处进行调用(实际上使用任何 ODR)以强制实例化。
当表达式依赖于 class 类型本身时,有没有办法在 class 中 static_assert?也许延迟评估直到类型完成或模板实例化之后?
示例代码:
#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;
}
您可以定义一个析构函数并将 static_assert 放在那里,至少对于 g++ 5.4 -std=c++11 这是有效的。
(从评论中移动)
您可以添加一个中间 class 来保持逻辑,并让您的客户实例化一个派生的,它只包含 static_assert
s
#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
};
这是一个使用助手 class 和间接类型别名的解决方案。我相信这没有缺点。
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;
我也在寻找 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;
}
我不知道这是否完全合规,但两者都 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
放在其中一个函数体中。成员函数体在 class 定义结束后计算,因此看不到不完整的类型。
对于模板,问题是并非所有成员函数都总是被实例化。非实例化成员函数中的 static_assert
不会触发。您需要在代码中的某处进行调用(实际上使用任何 ODR)以强制实例化。