如果不存在,则调用自由函数而不是方法

时间:2014-01-29 20:20:57

标签: c++ templates c++11

假设您有一系列类型无关的类通过返回值的给定方法实现一个通用概念:

class A { public: int val() const { ... } };
class B { public: int val() const { ... } };

假设您需要一个通用的自由函数,使T为任何未实现val方法的类型返回常规值,或者为所有具有一个类型的类型调用val方法:

template<class T> int val_of(const T& t) { return 0; }
template<class T> int val_of(const T& t) { return t.val(); }

考虑A和B只是样本:您不知道实现val将存在多少类型,以及有多少类型将不存在(因此明确的专业化赢得了&t; t刻度)。

是否有一种基于C ++标准的简单方法来静态选择val_of版本?

我在想std::conditionalstd::enable_if,但我找不到表达这种情况的简单方法。

4 个答案:

答案 0 :(得分:16)

您可以使用返回类型SFINAE:

template<typename T>
auto val_of(const T& t) -> decltype(std::declval<T>().val())
{
    return t.val();
}

int val_of(...)
{
    return 0;
}

答案 1 :(得分:4)

只是稍微有点评论......你的问题已得到解答。但我最近遇到了类似的问题。假设您要编写一个方法来将字符串打印到cout:使用成员函数write(std::cout),如果不可用则使用自由函数to_string(),如果不可用则回退到operator<<。您可以在答案中使用表达式SFINAE,并使用一个小类层次结构来消除重载的歧义:

struct S3 {};
struct S2 : S3 {};
struct S1 : S2 {};

template <class T>
auto print(S1, T const& t) -> decltype(t.write(std::cout)) {
    t.write(std::cout);
}

template <class T>
auto print(S2, T const& t) -> decltype(std::cout << to_string(t)) {
    std::cout << to_string(t);
}

template <class T>
void print(S3, T const& t) {
    std::cout << t;
}

template <class T>
void print(T const& t) {
    print(S1(), t);
}

答案 2 :(得分:3)

使用此类型特征类来确定类型是否具有val成员函数。

template<typename U>
struct has_val {
    typedef char yes;
    typedef struct { char c[2]; } no;

    template<typename X>
    static constexpr auto test(int) -> decltype( std::declval<X>().val(), yes() );

    template<typename X>
    static constexpr no test(...);

    static constexpr bool value = sizeof(test<U>(0)) == sizeof(yes);
};

您可以将其用作enable_if的条件。

答案 3 :(得分:1)

当前最高的投票答案会在某些情况下调用未定义的行为,因此我会给出另一种答案。

我们从一些样板机械开始:

template<typename T> struct type_sink { typedef void type; }
template<typename T> using TypeSink = typename type_sink<T>::type;

然后是has_val特征类:

template<typename T, typename=void>
struct has_val : std::false_type;
template<typename T>
struct has_val<T, TypeSink< decltype(std::declval<T>().val()) > > : std::true_type;

然后我们可以使用标签调度来解决您的问题:

template<typename T>
int val_of_internal( T const& t, std::true_type /* has_val */ ) {
  return t.val();
}
template<typename T>
int val_of_internal( T const& t, std::false_type /* has_val */ ) {
  return 0;
}
template<typename T>
int val_of( T const& t ) {
  return val_of_internal( t, has_val<T const>() );
}

如果您发现编写has_val令人厌烦并且更喜欢宏,那么这里有一组为您编写has_val的宏:

#define EXPRESSION_IS_VALID_FOR_TYPE_T_TRAIT( TRAIT_NAME, ... ) \
template<typename T, typename=void> \
struct TRAIT_NAME : std::false_type {}; \
template<typename T> \
struct TRAIT_NAME< T, TypeSink< decltype( __VA_ARGS__ ) > >: std::true_type {}

#define HAS_NULLARY_METHOD_TRAIT(TRAIT_NAME, METHOD_NAME) \
EXPRESSION_IS_VALID_FOR_TYPE_T_TRAIT( TRAIT_NAME, std::declval<T>().METHOD_NAME() )

现在我们可以写下这个:

HAS_NULLARY_METHOD_TRAIT( has_val, val );

但我不知道这是否值得。