Reputation: 9200
Is there a way to static_assert inside a class when the expression depends on the class type itself? Maybe delay the evaluation until the type is complete or after template instantiation?
Example code:
#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;
}
Upvotes: 6
Views: 1823
Reputation: 39141
I don't know if that's fully compliant but both 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;
}
It doesn't work when using the dtor instead of the ctor. YMMV.
This makes your type a non-aggregate though.
If your type has any mandatory member functions, you can put the static_assert
in one of those function bodies. Member function bodies are evaluated after the end of the class definition, and therefore don't see an incomplete type.
For templates, the issue is that not all member functions are always instantiated. A static_assert
in a non-instantiated member function won't fire. You need a call (any ODR-use actually) somewhere in the code to force instantiation.
Upvotes: 0
Reputation: 333
I was also looking for a solution with static_assert, but constraints work too:
#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;
}
Upvotes: 2
Reputation: 9200
Here's a solution using a helper class and a type alias for indirection. I believe this has no drawbacks.
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;
edit: Alternatively TestHelper can be moved into 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;
Upvotes: 2
Reputation: 126867
(moving from the comments)
You can add an intermediate class to keep the logic, and let your clients instantiate a derived one, which just contains the 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
};
Upvotes: 0
Reputation: 529
You can define a destructor and put the static_assert there, at least with g++ 5.4 -std=c++11 this works.
Upvotes: 0