c++ - 模板函数的奇怪输出的解释

标签 c++ templates c++11 variadic-templates sfinae

有人可以解释一下这里奇怪的输出吗?

#include <iostream>
#include <type_traits>

template <typename T>
constexpr auto has_foo_impl(int) -> decltype(typename T::foo{}, std::true_type{});

template <typename T>
constexpr auto has_foo_impl(long) -> std::false_type;

template <typename T>
constexpr bool has_foo() { return decltype(has_foo_impl<T>(0))::value; }

template <typename...> struct rank;

template <typename First, typename... Rest>
struct rank<First, Rest...> : rank<Rest...> {};

template <> struct rank<> {};

template <typename T, typename... Rest, typename... Args>
auto check (rank<T, Rest...>, Args... args) -> std::enable_if_t<has_foo<T>(), decltype(T(args...))> {
    return T(args...);
}

//template <typename T, typename U, typename... Args>
//auto check (rank<T,U>, Args... args) -> std::enable_if_t<has_foo<T>(), decltype(T(args...))> {
//  return T(args...);
//}
//
//template <typename T, typename... Args>
//auto check (rank<T>, Args... args) -> std::enable_if_t<has_foo<T>(), decltype(T(args...))> {
//  return T(args...);
//}

template <typename... Args>
auto check (rank<>, Args...) { std::cout << "Nothing found.\n"; }

template <typename... Ts>
struct Factory {
    template <typename... Args>
    decltype(auto) create (Args... args) const {
        return check(rank<Ts...>{}, args...);
    }
};

struct Object {};

struct Thing {};

struct Blob {
    using foo = int;
    Blob (int, double) { print(); }
    void print() const { std::cout << "Blob\n"; }
};

int main() {
    Factory<Blob, Object, Thing>().create(4,3.5);  // Blob
    Factory<Object, Blob, Thing>().create(4,3.5);  // Nothing found
    Factory<Object, Thing, Blob>().create(4,3.5);  // Nothing found
}

我希望看到Blob输出了3次。当我取消注释注释掉的重载 check 时,不过我确实明白了。单个变量不应该 check函数照顾我注释掉的那些吗?毕竟,rank<First, Rest...>源自rank<Rest...> .

我知道还有其他方法可以完成相同的工作,但我想知道为什么这种排名方法在这里不起作用。那Nothing found输出意味着rank<>通过了,也就是说中级也通过了。

最佳答案

你只能咬一口樱桃。

执行重载解析时,每个可用函数模板仅推导一次其模板参数,即使 SFINAE 删除了推导的重载,并且可能有其他(不太优选的)方法来推导模板参数。

所以,鉴于:

template <typename T, typename... Rest, typename... Args>
auto check (rank<T, Rest...>, Args... args) -> std::enable_if_t<has_foo<T>(), decltype(T(args...))>;

rank<Object, Blob, Thing>作为第一个参数类型,T推导为ObjectRest[Blob, Thing] 。只有在此之后,SFINAE 才会启动并消除推导的过载。

取消注释注释掉的重载使其工作的事实纯属巧合,因为这提供了 3 个函数模板,使其与 Blob 一起工作。在第一个、倒数第二个和最后一个位置;并且您已经用 3 个参数对其进行了测试。它不适用于 Object, Blob, Thing, WhateverBlob在 4 的第二个位置。( Example. )

此外,取消注释已注释掉的重载在 clang 中根本不起作用,这似乎对继承的模板推导进行了稍微不同的排序。 (Example.)

你需要产生更多的模板参数推导机会;一种方法可能是递归( Example ):

template <typename T, typename... Rest, typename... Args>
auto check (rank<T, Rest...>, Args... args) -> std::enable_if_t<!has_foo<T>(), decltype(check(rank<Rest...>{}, args...))> {
    return check(rank<Rest...>{}, args...);
}

关于c++ - 模板函数的奇怪输出的解释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36604131/

相关文章:

c++ - "warning: control reaches end of non-void function [-Wreturn-type]"是否有合理的修复?

c++ - 包括QT包

c++ - 在 C++ 上的 if 语句中构造函数初始化后删除字符串

c++ - pplx::守护进程未执行的任务

c# - 文本框 Silverlight 的样式

c++11 - boost::asio::async_connect 超时等待失败 (std::future::wait_for)

c++ - 为什么指针访问比 vector::iterator 访问慢? (编译器代码生成)

visual-studio-2010 - Visual Studio如何生成类模板?

c++ - 模板多态性

c++ - std::unique_ptr、删除器和 Win32 API