【问题标题】:Detect idiom with function failing static_assert检测函数失败的习语 static_assert
【发布时间】:2025-12-15 21:40:02
【问题描述】:

如果函数因static_assert 而失败,有没有办法使用detection idiom(或其他方法)来测试函数是否对给定的模板参数有效?

下面的例子说明foo(返回类型计算失败)的有效性被检测到,但bar(失败static_assert)的有效性不是。

#include <iostream>
#include <type_traits>

template <typename... T> using void_t = void;

template <class AlwaysVoid, template<class...> class Op, class... Args>
struct detector: std::false_type { };

template <template<class...> class Op, class... Args>
struct detector<void_t<Op<Args...>>, Op, Args...>: std::true_type { };

template <template<class...> class Op, class... Args>
constexpr bool is_detected = detector<void, Op, Args...>::value;

template <typename T>
std::enable_if_t<!std::is_void<T>::value> foo() {
  std::cout << "foo" << std::endl;
}

template <typename T>
void bar() {
  static_assert( !std::is_void<T>::value );
  std::cout << "bar" << std::endl;
}

template <typename T> using foo_t = decltype(foo<T>());
template <typename T> using bar_t = decltype(bar<T>());

int main(int argc, char* argv[]) {

  foo<int>();
  // foo<void>(); // fails as expected

  bar<int>();
  // bar<void>(); // fails as expected

  std::cout << std::boolalpha;

  // detection works for foo
  std::cout << is_detected<foo_t,int > << std::endl; // true
  std::cout << is_detected<foo_t,void> << std::endl; // false

  // but not for bar
  std::cout << is_detected<bar_t,int > << std::endl; // true
  std::cout << is_detected<bar_t,void> << std::endl; // true !!!
}

这就是我无法检测 boost::lexical_cast 是否对给定类型有效的原因。

【问题讨论】:

    标签: c++ c++14 sfinae static-assert


    【解决方案1】:

    这里不可能使用 SFINAE 来获得正确的输出,因为SFINAE rules 操作的是声明,而不是定义。

    声明的bar 的类型将始终为void(void),因此就SFINAE 而言,声明是可以的。

    如果你写了一个真正的检测成语(如I did here),并像这样使用它:

    template <typename T> 
    using CanCallFoo_t = decltype(&foo<T>);
    
    template<class T>
    using CanCallFoo = detect<T, CanCallFoo_t, void>;
    
    template<class T>
    using CanCallBar_t = decltype(&bar<T>);
    
    template< class T>
    using
    CanCallBar = detect<T, CanCallBar_t, void>;
    
    //...
    std::cout << CanCallFoo<int>::value << std::endl; // true
    std::cout << CanCallFoo<void>::value << std::endl; // false
    
    std::cout << CanCallBar<int>::value << std::endl;
    std::cout << CanCallBar<void>::value << std::endl;
    

    您会注意到 SFINAE 成功,然后在解析定义时出现编译器错误。

    错误:静态断言失败
    static_assert( !std::is_void&lt;T&gt;::value );

    Demo

    请注意,它适用于 foo,因为 foo 的声明类型将通过 void 的 SFINAE 失败

    static_assert 的目的是在没有找到其他更好的匹配时使编译失败,而不是作为 SFINAE 的替代品。

    【讨论】:

      【解决方案2】:

      Andy 的回答是正确的,他解释说使用 sfinae 无法检测到 static_assert 或任何定义替换失败。不过,我想指出的是,有可能解决您的问题,尽管您需要承受一些重复的冲击。

      基本上,您需要找出lexical_cast 尝试将哪些类型的操作应用于泛型类型。然后你用你自己的函数包装lexical_cast,并在lexical_cast需要的任何属性上定义你的函数。这并不优雅,但我怀疑与您感兴趣的应用程序相关的类型有很多要求,因此它是一个实用的解决方案(也许)。

      也许这很明显,但我想我会提到这一点,因为它尚未涵盖。

      【讨论】: