c++ - SFINAE | 奇怪的行为
问题描述
我正在学习 SFINAE 和 c++。我的 SFINAE 宏(这里称为“注释”)有一个奇怪的行为:
<lang/Annotations.h>
#pragma once
#define ENABLE_IF(y) typename std::enable_if<y,std::nullptr_t>::type
#define IS_REFERENCE(x) std::is_reference<x>::value
#define IS_CONSTRUCTIBLE(...) std::is_constructible<__VA_ARGS__>::value
我制作了一个自定义的“ MY_OBJECT
”类,它提供了以下构造函数:
MY_OBJECT(const char* stringDataPtr) noexcept;
这里的目标如下:
通过使用可变参数模板函数,必须检查每个模板参数的类型:如果可以将其传递给
String
构造函数 (std::is_constructible
),则必须打印消息“可构造”,否则必须打印“不可构造”。
问题
即使通过传递int
值,我的 SFINAE 方法也不会得到“SFINAE'd”,而且我总是得到“Isconstructible”消息。
<util/SFINAETools.h
namespace SFINAETools {
enum class SFINAEResult {
IS_CONSTRUCTIBLE,
IS_NOT_CONSTRUCTIBLE,
IS_REFERENCE,
IS_NOT_REFERENCE
};
std::ostream& operator<<(std::ostream& out, const SFINAEResult& value) {
static std::unordered_map<SFINAEResult, System::MyString> strings {
{SFINAEResult::IS_CONSTRUCTIBLE, "IS_CONSTRUCTIBLE"},
{SFINAEResult::IS_NOT_CONSTRUCTIBLE, "IS_NOT_CONSTRUCTIBLE"},
{SFINAEResult::IS_REFERENCE, "IS_REFERENCE"},
{SFINAEResult::IS_NOT_REFERENCE, "IS_NOT_REFERENCE"}
};
return out << strings[value];
}
class SFINAECallbackHandler : public Object {
public:
virtual void onSFINAEResult(const SFINAEResult& result) const = 0;
};
template <typename... ARGS, ENABLE_IF(IS_CONSTRUCTIBLE(ARGS...))>
void executeIfConstructible(const SFINAECallbackHandler& callBackHandler) {
callBackHandler.onSFINAEResult(SFINAEResult::IS_CONSTRUCTIBLE);
}
template <typename... ARGS, ENABLE_IF(!IS_CONSTRUCTIBLE(ARGS...))>
void executeIfConstructible(const SFINAECallbackHandler& callBackHandler) {
callBackHandler.onSFINAEResult(SFINAEResult::IS_NOT_CONSTRUCTIBLE);
}
template <typename X, ENABLE_IF(IS_REFERENCE(X))>
void executeIfIsReference(const SFINAECallbackHandler& callBackHandler) {
callBackHandler.onSFINAEResult(SFINAEResult::IS_REFERENCE);
}
template <typename X, ENABLE_IF(!IS_REFERENCE(X))>
void executeIfIsReference(const SFINAECallbackHandler& callBackHandler) {
callBackHandler.onSFINAEResult(SFINAEResult::IS_NOT_REFERENCE);
}
};
主文件
#include <lang/CppApplication.h>
#include <util/SFINAETools.h>
class MyCallbackHandler :public SFINAETools::SFINAECallbackHandler {
public:
virtual void onSFINAEResult(const SFINAETools::SFINAEResult& result) const override {
std::cout << result << std::endl;
}
};
class MY_OBJECT : public Object {
public:
MY_OBJECT(const char* strDataPtr) {
}
};
class Main : public CppApplication {
public:
virtual int main(const std::vector<String>& arguments) override {
createString(1, "2");
return 0;
}
template <typename Arg1>
void createString(Arg1&& arg1) {
SFINAETools::executeIfConstructible<MY_OBJECT, Arg1>(MyCallbackHandler());
}
template <typename Arg1, typename ...Args>
void createString(Arg1&& arg1, Args&&... args) {
createString(arg1);
createString(args...);
}
template <typename ...Args>
void createString(Args&&... args) {
std::list<MY_OBJECT> list;
createString(list, args...);
}
};
解决方案
我不知道这是否是问题(唯一的问题)但是这个宏
#define ENABLE_IF(y) typename std::enable_if<y>::type
变为void
当y
为真。
所以什么时候y
是真的
template <typename... Types, ENABLE_IF(!IS_CONSTRUCTIBLE(Types...)) = 0>
变成
template <typename... Types, void = 0>
那是行不通的。0
不是 的有效值void
。没有有效值void
。
和
template <typename X, ENABLE_IF(IS_REFERENCE(X))>
变成
template <typename X, void>
那更糟。
我想你可以定义ENABLE_IF
返回(在这种情况下)int
// ...........................................VVVVV
#define ENABLE_IF(y) typename std::enable_if<y, int>::type
记住= 0
每一次之后ENABLE_IF
另一个问题:现在你有
template <typename X, typename Y>
static bool executeIfConstructible(std::function<void()> predicate) {
predicate();
return true;
}
template <typename X, typename Y , ENABLE_IF(!IS_CONSTRUCTIBLE(X,Y))>
static bool executeIfConstructible(std::function<void()> predicate) {
return false;
}
因此,您有两个版本executeIfContructible()
:第一个始终启用,第二个仅在!IS_CONSTRUCTIBLE(X,Y)
为 true 时启用。
!IS_CONSTRUCTIBLE(X,Y)
您必须在为 false 时禁用第一个(当IS_CONSTRUCTIBLE(X,Y)
),否则在启用第二个时您将有一个模棱两可的呼叫。
template <typename X, typename Y , ENABLE_IF(IS_CONSTRUCTIBLE(X,Y))>
static bool executeIfConstructible(std::function<void()> predicate) {
predicate();
return true;
}
template <typename X, typename Y , ENABLE_IF(!IS_CONSTRUCTIBLE(X,Y))>
static bool executeIfConstructible(std::function<void()> predicate) {
return false;
}
未经请求的建议:C 风格的宏是提炼出来的邪恶。尽可能避免使用 C 风格的宏。
例如,代替宏ENABLE_IF
,定义一个using
template <bool B>
using EnableIf = typename std::enable_if<B, int>::type;
对于IS_REFERENCE
和IS_CONSTRUCTIBLE
——如果你确定需要它们——你可以定义(从 C++14 开始)几个constexpr
模板变量
template <bool B>
constexpr bool IsReference = std::is_reference<B>::value;
template <typename ... Ts>
constexpr bool IsConstructible = std::is_constructible<Ts...>::value;