用于用户定义转换的 SFINAE

SFINAE for user-defined conversion

这个问题真的没什么背景。

有很多方法可以使用 SFINAE(直接或间接使用 type_traits)来检查现有函数、成员函数等。但是:

第一个问题:有什么方法可以检查class是否实现了特定的用户定义的转换运算符?

为了说明我的意思,请考虑这段代码。我想 运行 此代码没有任何断言失败:

#include <type_traits>
#include <cassert>

struct NotADouble {
};
struct Double {
    // explicit or not - you decide
    explicit operator double() {
        return 1.;
    }
};
// CORRECT THIS: ...
template<typename T,
    class = decltype(static_cast<double>(std::declval<T>()))>
int f(T) {
    return 1;
}
int f(...) {
    return 2;
}
// ... UNTIL HERE

int main() {
    assert(f(NotADouble()) == 2);
    assert(f(Double()) == 1);
    assert(f(3.) == 2);
    assert(f(3) == 2);
    assert(f(3.f) == 2);
}

f的当前实现检查是否存在从Tdouble的任何标准转换序列,我假设这与本例中的std::is_convertible相同.

另一种方法是以下实现,它接受前两个测试。

template<typename T>
int f(T, double (T::*)() = nullptr) {
    return 1;
}
int f(...) {
    return 2;
}

问题2:虽然NotADouble没有实现任何转换运算符,但似乎允许这个成员函数指针。因此,double (T::*)() 到底是什么?为什么 class 存在?

第一个问题:您可以创建辅助结构来检查给定结构(c++14 代码)中是否定义了转换运算符:

#include <type_traits>
#include <iostream>

struct NotDouble { };
struct Double {
   explicit operator double() {
       return 1.0;
   }
};

template <class From, class To, class = void>
struct HasConversionOperator: std::false_type { };

template <class From, class To>
struct HasConversionOperator<From, To, decltype((&From::operator To), void())>: std::true_type { };

template <class From, class To, class = void>
struct HasExplicitConversion: std::false_type {};

template <class From, class To>
struct HasExplicitConversion<From, To, decltype(std::declval<To&>() = (To)std::declval<From&>(), void())>: std::true_type { };

template <class From, class To, class = void>
struct HasImplicitConversion: std::false_type {};

template <class From, class To>
struct HasImplicitConversion<From, To, decltype(std::declval<To&>() = std::declval<From&>(), void())>: std::true_type { };

template <class T>
std::enable_if_t<HasConversionOperator<T, double>::value && HasExplicitConversion<T, double>::value && !HasImplicitConversion<T, double>::value> is_double(T d) {
   std::cout << "has conversion to double" << std::endl;
}

template <class T>
std::enable_if_t<!HasConversionOperator<T, double>::value || !HasExplicitConversion<T, double>::value || HasImplicitConversion<T, double>::value> is_double(T) {
   std::cout << "don't have conversion to double" << std::endl;
}

int main() {
   is_double(Double{});
   is_double(NotDouble{});
}

输出:

has conversion to double
don't have conversion to double

第二个问题: double (T::*)()是任意成员函数指针的类型,拥有者T是returns不带的double任何参数。请记住,不仅转换运算符具有这样的签名。更重要的是,即使 class T 没有任何 returns double 的成员函数并且不带参数,它完全允许创建这样的指针类型,只是无法填充变量具有任何值的指针类型...