只有在其他重载失败时才使用模板重载

Only use templated overload when other overloads fail

本文关键字:重载 失败 其他      更新时间:2023-10-16

是否有一种方法可以确保除非所有其他重载都失败,否则不会选择模板重载,而无需求助于enable_if?

Int应该由长重载处理,但它是由模板重载处理的,编译器不喜欢这样。

class SetProxy {
public:
  void operator=(const TemplateString& value) {
    dict_.SetValue(variable_, value);
  }
  template<class T>
  void operator=(const T& value) {
    dict_.SetValue(variable_, TemplateString(value.data(), value.size()));
  }
  void operator=(long value) {
    dict_.SetIntValue(variable_, value);
  }
}

为什么int会由long重载处理?它们不是同一类型,具有T = int的模板与int完全匹配,而long不是完全匹配。

你能给我们更多关于你试图解决的潜在问题的信息吗?

问题中代码的问题是,如果传递了可转换为longTemplateString的参数,则模板是最佳匹配:模板不需要进行转换,而调用其他函数则需要进行转换。

即使不使用std::enable_if也可以实现所需的行为,尽管我认为禁止使用std::enable_if是一个虚构的要求:即使你不能使用C++2011或Boost,实现std::enable_if也非常简单。实现一些想要的类型特征有点困难,但却是可行的。有效地限制它的使用意味着你需要以一种或多或少做作的方式实现本质上相同的逻辑,但没有说明它真正的作用。例如,此代码不使用std::enable_if或SFINAE,但实际创建了一个额外的对象,如果使用了SFINAE则不需要该对象:

#include <iostream>
#include <string>
class SetProxy {
    template <typename F>
    struct helper {
        helper(F const& v): value_(v) {}
        F const& value_;
    };
    template<typename F>
    void aux(helper<F> value, ...) {
        std::cout << "template " << value.value_ << "n";
    }
    template<typename F>
    void aux(long value, int) {
        std::cout << "long: " << value << "n";
    }
    template<typename F>
    void aux(std::string const& value, int) {
        std::cout << "string: " << value << "n";
    }
public:
    template<typename T>
    void operator=(const T& value) {
        this->aux<T>(value, 0);
    }
};
struct foo {};
std::ostream& operator<< (std::ostream& out, foo const&) {
    return out << "foo";
}
int main()
{
    SetProxy p;
    p = 17l;
    p = 17;
    p = foo();
    p = "hello";
    p = std::string("hello");
}

它没有使用原始问题中的类型,因为我无法访问它们,而且我不想键入与实际问题无关的内容。注意,这有效地包含了std::is_convertible的实现的重要比特。在内部,有必要转发到另一个模板,因为赋值运算符不能有变量参数列表:由于有从inthelper<int>的可行转换,如果没有其他东西来区分类型,就会有歧义,因此使用变量参数列表会使模板版本更差。

仅供参考,在我看来,这是使用std::enable_if:的可读性更强的版本

class SetProxy {
public:
    template <typename T>
    typename std::enable_if<!std::is_convertible<T, long>::value
                            && !std::is_convertible<T, std::string>::value, void>::type
    operator= (T const& value) {
        std::cout << "template: " << value << "n";
    }
    void operator= (long value) {
        std::cout << "long: " << value << "n";
    }
    void operator= (std::string value) {
        std::cout << "std::string: '" << value << "'n";
    }
};

int与这里的模板版本匹配并不意外。模板参数匹配将获得const int&的正匹配,并将其传递给重载解析。const int&long更适合int。如果你想避免为int类型调用模板版本,那么我建议你为int添加一个显式重载。