即使函数签名无效,也会触发static_assert

时间:2016-03-23 10:59:20

标签: c++

假设我正在设计一个提供某些类型和模板函数的库,我希望用户可以在这些类型上实例化。模板函数的签名取决于模板参数中的嵌套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之前,显而易见,现在它需要一些奇怪的特质魔法。还有更好的方法吗?

1 个答案:

答案 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));
}

LIVE

编辑:

或者,您可以为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
}

LIVE