独立源文件中的 SFINAE 模板专业化
SFINAE Template Specialization in Separate Source File
我正在维护一个程序,该程序具有许多基本相似的不同结构。我想编写一个模板方法,利用 SFINAE 启用从客户端调用此方法。当我内联定义模板专业化时,一切都按预期进行。但是,当我尝试将模板定义移动到单独的编译单元时,我 运行 遇到了问题。我正在尝试将模板实现移动到一个单独的文件中,以便为大多数依赖 类 启用前向声明。以下是我试图实现的示例:
// Impl.h
#pragma once
struct A {
struct {
int value;
} valueA;
};
struct B {
struct {
int value;
} valueB;
};
template<typename T>
int GetValue(T const &value);
// Impl.cpp
#include "Impl.h"
#include <type_traits>
using std::enable_if_t;
using std::remove_reference_t;
template<typename T, typename U, U(T::*Value)>
static inline int GetValueImpl(T const &value) {
return (value.*Value).value;
}
template<typename T>
enable_if_t<T::valueA, int> GetValue(T const &value) {
static constexpr auto T::*const Value = &T::valueA;
typedef remove_reference_t<decltype(value.*Value)> ValueType;
return GetValueImpl<T, ValueType, Value>(value);
}
template<typename T>
enable_if_t<T::valueB, int> GetValue(T const &value) {
static constexpr auto T::*const Value = &T::valueB;
typedef remove_reference_t<decltype(value.*Value)> ValueType;
return GetValueImpl<T, ValueType, Value>(value);
}
template<> int GetValue(A const &); // C2912 here
template<> int GetValue(B const &); // C2912 here
我正在使用 VS2017u2,但收到错误 C2912:显式特化 'int GetValue(const A &)' 不是函数模板的特化。有谁知道如何使用单独的编译单元中的定义使其工作?
当你写 enable_if_t<T::valueA, int>
时,它正在检查 T
的一个名为 valueA
的静态成员(可能是 static constexpr bool valueA = /* true or false */;
,比如 T::value
表示如果 T
是 using T = std::is_same<U, V>;
).
要实际检查它是否有名为 valueA
或 valueB
的成员,请将其置于如果该成员不存在或 [=22] 时将出现替换错误的上下文中=].类似于:
// Pointers to member variables can never be null
// so these will always be enabled if `valueA` or
// `valueB` exist in the first place
enable_if_t<&T::valueA != nullptr, int>
enable_if_t<&T::valueB != nullptr, int>
// But that also allows pointers to static members
// so if you don't want that, you can do something else.
// Like checking if `&T::member` is a pointer to a member
// (But this is probably overkill as you have a specific set
// of types and none of those names are ever static members
// and if you didn't there is a small caveat with
// an overloaded `operator&` but that doesn't really matter)
enable_if_t<std::is_same_v<decltype(&T::valueA), decltype(T::valueA) T::*>, int>
enable_if_t<std::is_same_v<decltype(&T::valueB), decltype(T::valueB) T::*>, int>
即使修复了 SFINAE 检查,您仍然使用错误的语法进行模板实例化。你应该使用:
template int GetValue(A const &); // No `<>`
template int GetValue(B const &);
之后,它仍然不起作用,因为 template<typename T> enable_if_t<..., int> GetValue(T const&);
和 template<typename T> int GetValue(T const&);
是不同的函数,所以它应该实例化哪一个是不明确的(因为两者都可以)。您需要将 和 都变成不同的函数(在我的示例中为 GetValueImpl2
),并且 GetValue
的声明方式与 header 中的相同:
#include <type_traits>
#include "Impl.h"
using std::enable_if_t;
using std::remove_reference_t;
template<typename T, typename U, U(T::*Value)>
static inline int GetValueImpl(T const &value) {
return (value.*Value).value;
}
template<typename T>
enable_if_t<std::is_same_v<decltype(&T::valueA), decltype(T::valueA) T::*>, int> GetValueImpl2(T const &value) {
static constexpr auto T::*const Value = &T::valueA;
using ValueType = decltype(T::valueA);
return GetValueImpl<T, ValueType, Value>(value);
}
template<typename T>
enable_if_t<std::is_same_v<decltype(&T::valueB), decltype(T::valueB) T::*>, int> GetValueImpl2(T const &value) {
static constexpr auto T::*const Value = &T::valueB;
using ValueType = decltype(T::valueB);
return GetValueImpl<T, ValueType, Value>(value);
}
template<typename T>
int GetValue(T const&value) {
return GetValueImpl2(value);
}
template int GetValue<A>(A const &);
template int GetValue<B>(B const &);
我正在维护一个程序,该程序具有许多基本相似的不同结构。我想编写一个模板方法,利用 SFINAE 启用从客户端调用此方法。当我内联定义模板专业化时,一切都按预期进行。但是,当我尝试将模板定义移动到单独的编译单元时,我 运行 遇到了问题。我正在尝试将模板实现移动到一个单独的文件中,以便为大多数依赖 类 启用前向声明。以下是我试图实现的示例:
// Impl.h
#pragma once
struct A {
struct {
int value;
} valueA;
};
struct B {
struct {
int value;
} valueB;
};
template<typename T>
int GetValue(T const &value);
// Impl.cpp
#include "Impl.h"
#include <type_traits>
using std::enable_if_t;
using std::remove_reference_t;
template<typename T, typename U, U(T::*Value)>
static inline int GetValueImpl(T const &value) {
return (value.*Value).value;
}
template<typename T>
enable_if_t<T::valueA, int> GetValue(T const &value) {
static constexpr auto T::*const Value = &T::valueA;
typedef remove_reference_t<decltype(value.*Value)> ValueType;
return GetValueImpl<T, ValueType, Value>(value);
}
template<typename T>
enable_if_t<T::valueB, int> GetValue(T const &value) {
static constexpr auto T::*const Value = &T::valueB;
typedef remove_reference_t<decltype(value.*Value)> ValueType;
return GetValueImpl<T, ValueType, Value>(value);
}
template<> int GetValue(A const &); // C2912 here
template<> int GetValue(B const &); // C2912 here
我正在使用 VS2017u2,但收到错误 C2912:显式特化 'int GetValue(const A &)' 不是函数模板的特化。有谁知道如何使用单独的编译单元中的定义使其工作?
当你写 enable_if_t<T::valueA, int>
时,它正在检查 T
的一个名为 valueA
的静态成员(可能是 static constexpr bool valueA = /* true or false */;
,比如 T::value
表示如果 T
是 using T = std::is_same<U, V>;
).
要实际检查它是否有名为 valueA
或 valueB
的成员,请将其置于如果该成员不存在或 [=22] 时将出现替换错误的上下文中=].类似于:
// Pointers to member variables can never be null
// so these will always be enabled if `valueA` or
// `valueB` exist in the first place
enable_if_t<&T::valueA != nullptr, int>
enable_if_t<&T::valueB != nullptr, int>
// But that also allows pointers to static members
// so if you don't want that, you can do something else.
// Like checking if `&T::member` is a pointer to a member
// (But this is probably overkill as you have a specific set
// of types and none of those names are ever static members
// and if you didn't there is a small caveat with
// an overloaded `operator&` but that doesn't really matter)
enable_if_t<std::is_same_v<decltype(&T::valueA), decltype(T::valueA) T::*>, int>
enable_if_t<std::is_same_v<decltype(&T::valueB), decltype(T::valueB) T::*>, int>
即使修复了 SFINAE 检查,您仍然使用错误的语法进行模板实例化。你应该使用:
template int GetValue(A const &); // No `<>`
template int GetValue(B const &);
之后,它仍然不起作用,因为 template<typename T> enable_if_t<..., int> GetValue(T const&);
和 template<typename T> int GetValue(T const&);
是不同的函数,所以它应该实例化哪一个是不明确的(因为两者都可以)。您需要将 和 都变成不同的函数(在我的示例中为 GetValueImpl2
),并且 GetValue
的声明方式与 header 中的相同:
#include <type_traits>
#include "Impl.h"
using std::enable_if_t;
using std::remove_reference_t;
template<typename T, typename U, U(T::*Value)>
static inline int GetValueImpl(T const &value) {
return (value.*Value).value;
}
template<typename T>
enable_if_t<std::is_same_v<decltype(&T::valueA), decltype(T::valueA) T::*>, int> GetValueImpl2(T const &value) {
static constexpr auto T::*const Value = &T::valueA;
using ValueType = decltype(T::valueA);
return GetValueImpl<T, ValueType, Value>(value);
}
template<typename T>
enable_if_t<std::is_same_v<decltype(&T::valueB), decltype(T::valueB) T::*>, int> GetValueImpl2(T const &value) {
static constexpr auto T::*const Value = &T::valueB;
using ValueType = decltype(T::valueB);
return GetValueImpl<T, ValueType, Value>(value);
}
template<typename T>
int GetValue(T const&value) {
return GetValueImpl2(value);
}
template int GetValue<A>(A const &);
template int GetValue<B>(B const &);