模板函数

时间:2016-04-13 16:15:49

标签: 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输出三次。当我取消注释已注释掉的重载check时,我确实得到了它。单个可变参数check函数不应该处理我注释掉的那些吗?毕竟,rank<First, Rest...>来自rank<Rest...>

我知道其他方法可以完成相同的工作,但我想知道为什么这种排名方法在这里不起作用。输出Nothing found意味着rank<>被传递,这意味着中间等级也被传递。

1 个答案:

答案 0 :(得分:4)

你只能吃一口樱桃。

执行重载解析时,每个可用的函数模板只获得一次推导出的模板参数,即使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, Whatever {4}的第二个位置Blob,它将无效。(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...);
}