将 std::enable_if 与外联成员函数和模板化静态成员条件一起使用
Using std::enable_if with out-of-line member functions and templated static member conditions
我想使用 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
时它确实编译得很好,就像直接使用 std::is_invocable_r_v
一样。我想避免这种情况,因为在我的真实代码中 Consumer
的签名非常复杂,这需要相当多的 copy/paste.
将 isConsumer
拉到 class 之外也不是一个选项,因为在我的真实代码中它依赖于 Container
内部的私有类型定义。它必须在 class.
内
这里如何正确使用std::enable_if
?
似乎确实没有办法根据当前声明进行 out-of-line 定义(gcc 抱怨声明使用了“匿名类型”)
可能的解决方法:
使用 static_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) {
// ...
}
改为使用 return 类型 SFINAE,以便您在 class 的命名空间中进行查找:
#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>>
{
// ...
}
当然,只需内联定义即可。
我想使用 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
时它确实编译得很好,就像直接使用 std::is_invocable_r_v
一样。我想避免这种情况,因为在我的真实代码中 Consumer
的签名非常复杂,这需要相当多的 copy/paste.
将 isConsumer
拉到 class 之外也不是一个选项,因为在我的真实代码中它依赖于 Container
内部的私有类型定义。它必须在 class.
这里如何正确使用std::enable_if
?
似乎确实没有办法根据当前声明进行 out-of-line 定义(gcc 抱怨声明使用了“匿名类型”)
可能的解决方法:
使用 static_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) {
// ...
}
改为使用 return 类型 SFINAE,以便您在 class 的命名空间中进行查找:
#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>>
{
// ...
}
当然,只需内联定义即可。