C++:使用 SFINAE 检测 Visitor 中的参数类型

问题描述 投票:0回答:1

我正在编写一个接受以下形式的访问者的函数:

struct Visitor {
    void operator()(int i) {...}
    void operator()(const std::string& s)  {...}
    void operator()(const auto& s)  {...}
};

当我访问的值是非常量字符串时,我想检测访问者函数arg的常量性。例如,如果有人经过这位访客:

struct Visitor {
    void operator()(const std::string& s)  {...}
    void operator()(const auto& s)  {...}
};

struct Visitor {
    void operator()(const auto& s)  {...}
};

我想检测到他们肯定会收到一个 const ref,而如果他们通过了:

struct Visitor {
    void operator()(std::string& s)  {...}
    void operator()(auto& s)  {...}
};

struct Visitor {
    void operator()(const std::string& s)  {...}
    void operator()(auto& s)  {...}
};

我想检测他们将收到非常量引用。

我试图通过依赖不明确的重载错误来检测结构是否包含具有特定参数类型的

operator()
来使用模板来执行此操作。例如我可以这样做(没有模板):

struct TestConstStringArg {
    void operator()(const std::string& str) {}
};

struct Tester : public TestConstStringArg, public Visitor {
    void tester() {
        std::string s;
        (*this)(s);
    }
};

如果

Visitor
也有
operator(const std::string&)
成员,这将给出编译错误,因为这将是一个不明确的函数解析。

我如何在模板中利用它来提供我可以检查的

constexpr bool
?目前我有这个:

template <class T, class ArgTest>
struct ArgTester : public T, public ArgTest {
    void tester() {
        std::string s;
        (*this)(s);
    }
};

template<class T>
struct VisitorArgTester {
    typedef char yes[1];
    typedef char no[2];

    template<class ArgTest> static no& test(ArgTester<T, ArgTest>* t);
    template<class ArgTest> static yes& test(...);

    static constexpr bool hasConstStr = sizeof(test<TestConstStringArg>(nullptr)) == sizeof(yes);
};


struct Visitor {
    void operator()(const std::string& str) {}
};
static_assert(VisitorArgTester<Visitor>::hasConstStr);

这里的想法是,如果 T 不包含 const string ref arg 版本,则返回

test()
no&
版本将编译正常,否则不明确的错误将导致使用
yes&
版本。但这似乎不起作用,无论
static_assert
是否有运算符,
Visitor
总是失败。我做错了什么?

c++ templates sfinae
1个回答
0
投票

TBH,我有点不清楚你到底需要什么。这种情况也比最初出现的情况更深入(更长的故事)。现在我只是在我的脑海中发布一个快速且(希望不是太)肮脏的解决方案,您可以将其用作-是或可能满足您的需求(尽管仍然不完美 - 例如无法区分 noexcept 成员,尽管它可能可以使用更复杂的方法来修复)。我不确定这是否是您所追求的(或类似的东西),但首先尝试评估情况。

单击此处运行它。

#include <type_traits>
#include <iostream>

template <typename C, typename F>
using FuncToMemberFuncPtr_t = F C::*;

template <typename, typename, typename = void>
struct HasOperator: std::false_type
{
};

template <typename C, typename F>
struct HasOperator<C,
                   F,
                   std::void_t<decltype(static_cast<FuncToMemberFuncPtr_t<C, F>>(&C::operator()))>
                  > : std::true_type
{
};

template <typename C, typename F>
inline constexpr bool HasOperator_v = HasOperator<C, F>::value;

template <typename C>
inline constexpr bool HasOperatorWithNonConstStringRef_v = HasOperator_v<C, void (std::string &) > ||
                                                           HasOperator_v<C, void (std::string &) const>;


template <typename C>
inline constexpr bool HasOperatorWithConstStringRef_v = HasOperator_v<C, void (const std::string &) > ||
                                                        HasOperator_v<C, void (const std::string &) const>;
struct Visitor1
{
    void operator()(int i);
    void operator()(std::string &);
};

struct Visitor2
{
    void operator()(int i);
    void operator()(const std::string &);
};

int main()
{
    std::cout << "Visitor1 -> void (std::string &): " << std::boolalpha << HasOperatorWithNonConstStringRef_v<Visitor1> << '\n'; // true
    std::cout << "Visitor1 -> void (const std::string &): " << std::boolalpha << HasOperatorWithConstStringRef_v<Visitor1> << '\n'; // false
    std::cout << "Visitor2 -> void (std::string &): " << std::boolalpha << HasOperatorWithNonConstStringRef_v<Visitor2> << '\n'; // false
    std::cout << "Visitor2 -> void (const std::string &): " << std::boolalpha << HasOperatorWithConstStringRef_v<Visitor2> << '\n'; // true

    return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.