首页 > 解决方案 > 将 std::enable_if 与外联成员函数和模板化静态成员条件一起使用

问题描述

我想使用 SFINAE 创建一个带有仿函数的模板化成员函数Consumer。某物是否是消费者取决于模板化的static constexpr bool isConsumer成员变量。我已将代码简化为以下示例:

#include <type_traits>

template <typename T>
struct Container {
    T data[100];

    template <typename Consumer>
    static constexpr bool isConsumer = std::is_invocable_r_v<void, Consumer, T>;

    template <typename Consumer, std::enable_if_t<isConsumer<Consumer>, int> = 0>
    void forEach(const Consumer &consumer);
};

template <typename T>
template <typename Consumer, std::enable_if_t<Container<T>::template isConsumer<Consumer>, int>>
void Container<T>::forEach(const Consumer &consumer)
{
    for (int i = 0; i < 100; ++i) {
        consumer(data[i]);
    }
}

由于我不理解的原因,这无法编译:

<source>:16:20: error: out-of-line definition of 'forEach' does not match any declaration in 'Container<T>'

void Container<T>::forEach(const Consumer &consumer)

                   ^~~~~~~

isConsumer当我直接使用inline 时,它​​确实编译得很好std::is_invocable_r_v。我想避免这种情况,因为在我的真实代码中,签名Consumer非常复杂,这需要大量的复制/粘贴。

拉出isConsumer类也不是一种选择,因为在我的真实代码中它依赖于内部的私有类型定义Container。它必须在类内。

我如何std::enable_if在这里正确使用?

标签: c++templatesc++17sfinaeenable-if

解决方案


给定当前声明,似乎确实没有办法做出不合时宜的定义(gcc 抱怨声明使用“匿名类型”)

可能的解决方法:

使用 astatic_assert代替 SFINAE:

#include <type_traits>

template <typename T>
struct Container {
    template <typename Consumer>
    static constexpr bool isConsumer = /* ... */;

    template <typename Consumer>
    void forEach(const Consumer &consumer);
};

template <typename T>
template <typename Consumer>
void Container<T>::forEach(const Consumer &consumer)
{
    static_assert(isConsumer<Consumer>);
    // ...
}

完全限定声明,因此定义应该引用相同的类型(这在 clang 中不起作用。似乎是一个 clang 错误):

#include <type_traits>

template <typename T>
struct Container {
    template <typename Consumer>
    static constexpr bool isConsumer = /* ... */;

    template <typename Consumer, std::enable_if_t<Container<T>::template isConsumer<Consumer>, int> = 0>
    void forEach(const Consumer &consumer);
};

template <typename T>
template <typename Consumer, std::enable_if_t<Container<T>::template isConsumer<Consumer>, int>>
void Container<T>::forEach(const Consumer &consumer)
{
    // ...
}

委托给具有小型转发内部函数的私有函数:

#include <type_traits>

template <typename T>
struct Container {
    template <typename Consumer>
    static constexpr bool isConsumer = /* ... */;

    template <typename Consumer, std::enable_if_t<isConsumer<Consumer>, int> = 0>
    void forEach(const Consumer &consumer) {
        forEachImpl(consumer);
    }
private:
    template<typename Consumer>
    void forEachImpl(const Consumer&);
};

template<typename T>
template<typename Consumer>
void Container<T>::forEachImpl(const Consumer& consumer) {
    // ...
}

请改用返回类型 SFINAE,这样您就可以在类的命名空间中进行查找:

#include <type_traits>

template <typename T>
struct Container {
    template <typename Consumer>
    static constexpr bool isConsumer = /* ... */;

    template <typename Consumer>
    std::enable_if_t<isConsumer<Consumer>> forEach(const Consumer &consumer);
};

template <typename T>
template <typename Consumer>
auto Container<T>::forEach(const Consumer &consumer) -> std::enable_if_t<isConsumer<Consumer>>
{
    // ...
}

当然,只是内联定义它。


推荐阅读