首页 > 解决方案 > 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...);
    }
};

标签: c++templatesvariadic-templatessfinae

解决方案


我不知道这是否是问题(唯一的问题)但是这个宏

#define ENABLE_IF(y) typename std::enable_if<y>::type

变为voidy为真。

所以什么时候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_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;

推荐阅读