科学学院 |奇怪的行为

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_REFERENCEIS_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;