【问题标题】:Explanation of strange output from template function模板函数奇怪输出的解释
【发布时间】:2016-08-04 21:17:56
【问题描述】:

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

#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&lt;First, Rest...&gt; 派生自 rank&lt;Rest...&gt;

我知道完成相同工作的其他方法,但我想知道为什么这种排名方法在这里不起作用。输出Nothing found表示通过了rank&lt;&gt;,也就是通过了中间等级。

【问题讨论】:

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


    【解决方案1】:

    樱桃只能咬一口。

    在执行重载解析时,每个可用的函数模板只推导出一次模板参数,即使 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&lt;Object, Blob, Thing&gt;作为第一个参数类型,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...);
    }
    

    【讨论】:

    猜你喜欢
    • 2020-07-24
    • 1970-01-01
    • 2017-04-06
    • 1970-01-01
    • 2021-03-27
    • 2013-01-10
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多