SFINAE 使基础模板总是导致错误

SFINAE to make base template always result in error

所以我正在设计一种 my_numeric_cast 函数来限制在使用我正在编写的框架时可用的转换类型。

做这样的事情非常简单

template<typename To, typename From>
constexpr To my_numeric_cast(From);

template<>
constexpr float my_numeric_cast<float, int>(int i) { return i; }

有效,只要使用强制转换,就只允许从整数转换为浮点数。并在尝试不在白名单中的转换时产生链接错误。

但是,我真的很想将其设为编译错误,以便更快地发现误用。
如何使基本模板主体有效,期望在实例化时?

您可以使用特征来获取编译时错误:

template<typename, typename> struct RetTraits;
// Enable it for int -> float
template<> struct RetTraits<int, float> { using type = float; };

template<typename To, typename From>
using RetType = typename RetTraits<To, From>::type;

template<typename To, typename From>
constexpr RetType<To, From> my_numeric_cast(From f) {
    return To(f);
}

int main() {
    my_numeric_cast<int, float>(42);
    // This won't compile
    // my_numeric_cast<int, int>(42);
}

您不能编写没有模板参数使函数体在 C++ 中有效的模板函数特化。这样做的结果是一个不需要诊断的错误程序。这包括初级专业。

所以这里的大部分答案都是未定义的行为。它们可能有效,但它们不是有效的 C++。他们今天可能工作,但在库升级、编译器升级或不同的构建目标之后,他们可能会以完全不同且令人惊讶的方式失败。在没有充分理由的情况下依赖 UB 是一个坏主意。

从好的方面来说,我们可以取消模板特化并一次性解决您的问题:

template<class T>struct tag_t{}; // may need `constexpr tag_t(){}` on some compilers
template<class T>constexpr tag_t<T> tag{};

template<class T, class F>
constexpr T my_numeric_cast(F, tag_t<F>)=delete; // generates compile time error

constexpr float my_numeric_cast(int i, tag_t<float>) { return i; } // not a template!  Could be if you want it to be.

template<typename To, typename From>
constexpr To my_numeric_cast(From f){
  return my_numeric_cast(f, tag<To>);
}

完成。

=delete 生成友好消息。程序结构良好。实施演员表不再是专业化。您甚至可以在启用 ADL 时在转换类型的命名空间中实现它。

如果您解决了模板函数专业化的问题,请重新考虑。它们很脆弱,不像 class 模板特化或函数重载那样工作(虽然看起来两者都像!),而且通常不是解决任何问题的最佳方案。也有例外情况,这可能是个好主意,但这种情况非常罕见,考虑到他们很少避免使用古怪的功能,这可能仍然值得。

这是一个更新的答案,可以在 c++11 之前的代码中使用。我仍然认为 Yakk 的回答以及他关于函数模板专业化的建议非常出色,但我觉得我应该 post 为了完整性。

如果理解正确,此解决方案不会导致程序格式错误,甚至会给出相当明确的错误消息。 它利用使用 class 模板将主模板标记为不完整的概念,但仍然使用函数模板特化。

namespace detail {
    template<typename T1, typename T2> struct InvalidInstantiation;
}

template<typename To, typename From>
To my_cast (From f) {
    detail::InvalidInstantiation<To, From> No_Function_Exists;
}

template<>
float my_cast(int i) { return i; }

int main() {
    my_cast<float>(7);
    //my_cast<int>(7.0); // This will result in an error
    return 0;
}