科学学院 |奇怪的行为
SFINAE | strange behaviour
我正在学习SFINAE和c++。我的 SFINAE 宏(此处称为“注释”)有一个奇怪的行为:
#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
”class,它提供了以下构造函数:
MY_OBJECT(const char* stringDataPtr) noexcept;
这里的objective如下:
By using a variadic template function, each template argument's type must be checked: if it can be passed to String
constructor (std::is_constructible
) then a message "is constructible" must be printed, otherwise "is not constructible" must be printed.
问题
即使传递 int
值,我的 SFINAE 方法也不会得到“SFINAE'd”并且我总是收到“可构造”消息。
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);
}
};
MAIN.cpp
#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
当 y
为真时, 变为 void
。
所以当 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
定义为 return(在这种情况下)和 int
// ...........................................VVVVV
#define ENABLE_IF(y) typename std::enable_if<y, int>::type
记住每个 ENABLE_IF
之后的 = 0
另一个问题:现在你有
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)
为真时启用。
当 !IS_CONSTRUCTIBLE(X,Y)
为假时(当 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;
我正在学习SFINAE和c++。我的 SFINAE 宏(此处称为“注释”)有一个奇怪的行为:
#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
”class,它提供了以下构造函数:
MY_OBJECT(const char* stringDataPtr) noexcept;
这里的objective如下:
By using a variadic template function, each template argument's type must be checked: if it can be passed to
String
constructor (std::is_constructible
) then a message "is constructible" must be printed, otherwise "is not constructible" must be printed.
问题
即使传递 int
值,我的 SFINAE 方法也不会得到“SFINAE'd”并且我总是收到“可构造”消息。
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);
}
};
MAIN.cpp
#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
当 y
为真时, 变为 void
。
所以当 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
定义为 return(在这种情况下)和 int
// ...........................................VVVVV
#define ENABLE_IF(y) typename std::enable_if<y, int>::type
记住每个 ENABLE_IF
= 0
另一个问题:现在你有
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)
为真时启用。
当 !IS_CONSTRUCTIBLE(X,Y)
为假时(当 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;