假设我正在设计一个提供某些类型和模板函数的库,我希望用户可以在这些类型上实例化。模板函数的签名取决于模板参数中的嵌套typedef,如果模板使用不兼容的类型实例化,我想使用static_assert
给出一个很好的错误消息:
// library.h
#include <type_traits>
struct compatible_with_f {};
struct foo : compatible_with_f {
using some_type = int;
};
struct bar : compatible_with_f {
using some_type = float;
};
template <typename T>
void f(typename T::some_type param) {
static_assert(std::is_base_of<compatible_with_f, T>::value,
"the template parameter to f should be `foo` or `bar`");
// some code, which also uses the type T itself directly
}
// main.cc
int main() {
f<foo>(0); // works
f<bar>(0.f); // works
f<int>(0); // error: no matching function for call to 'f'
}
这不能正常工作:我的友好错误消息没有打印,因为f
的签名没有意义,所以编译器在它到达{{1在函数体中。
有没有办法以一种好的方式实现我想要的东西?这是我能想到的最好的:
static_assert
我使用SFINAE提供可以从任何东西转换的默认// library.h
#include <type_traits>
struct compatible_with_f {};
struct foo : compatible_with_f {
using some_type = int;
};
struct anything {
template <typename T>
anything(T&&) {}
};
template <typename, typename = void>
struct get_some_type {
using type = anything;
};
template <typename T>
struct get_some_type<T, typename std::enable_if<std::is_base_of<compatible_with_f, T>::value>::type> {
using type = typename T::some_type;
};
template <typename T>
void f(typename get_some_type<T>::type param) {
static_assert(std::is_base_of<compatible_with_f, T>::value,
"the template parameter to f should be `foo` or `bar`");
// some code, which also uses the type T itself directly
}
// main.cc
int main() {
f<foo>(0); // works
f<int>(0); // error: the template parameter to f should be `foo` or `bar`
}
。现在签名总是有意义的,所以编译器实例化函数的主体,我得到了很好的错误消息。但是我不喜欢这个解决方案:它看起来过于复杂,而且它有一个很大的缺点,即some_type
的签名现在已经不那么明确了 - 在它f
之前,显而易见,现在它需要一些奇怪的特质魔法。还有更好的方法吗?
答案 0 :(得分:0)
您可以编写转发函数进行检查,然后再将其传递给实际实现:
template <typename T>
void f_impl(typename T::some_type param) {
// some code, which also uses the type T itself directly
}
template<typename U, typename T>
void f(T&& t)
{
static_assert(std::is_base_of<compatible_with_f, U>::value,
"the template parameter to f should be `foo` or `bar`");
f_impl<U>(std::forward<T>(t));
}
编辑:
或者,您可以为f
添加一个不太专业的重载:
template <typename T>
void f(T param) {
static_assert(std::is_base_of<compatible_with_f, T>::value,
"the template parameter to f should be `foo` or `bar`");
}
template <typename T>
void f(typename T::some_type param) {
static_assert(std::is_base_of<compatible_with_f, T>::value,
"the template parameter to f should be `foo` or `bar`");
// some code, which also uses the type T itself directly
}