在以下代码中,我想替换
template <typename T, typename... Args>
auto check (rank<1,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T, Args...>(), decltype(check(rank<2, Ts...>{}, args...))> {
return check(rank<2, Ts...>{}, args...); // Since rank<1,T> derives immediately from rank<2, Ts...>.
}
template <typename T, typename... Args>
auto check (rank<2,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T, Args...>(), decltype(check(rank<3, Ts...>{}, args...))> {
return check(rank<3, Ts...>{}, args...); // Since rank<2,T> derives immediately from rank<3, Ts...>.
}
// etc... until rank<9>.
简单
template <std::size_t N, typename T, typename... Args>
auto check (rank<N,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T, Args...>(), decltype(check(rank<N+1, Ts...>{}, args...))> {
return check(rank<N+1, Ts...>{}, args...); // Since rank<N,T> derives immediately from rank<N+1, Ts...>.
}
template <typename T, typename... Args>
auto check (rank<10, T>, Args... args) const { std::cout << "Nothing found.\n"; }
但是当我这样做时,尽管终止check(rank<10, T>, Args... args)
函数,模板实例仍会继续。以下是使用上面的长版本的完整代码。很抱歉没有尽量减少问题,因为我不认为我可以最小化它并显示问题所在。跳转到main()将显示我之后的简单任务,但我想使用序列排序和重载解析来解决它。
#include <iostream>
#include <type_traits>
#include <tuple>
template <typename T, typename... Args>
constexpr auto has_argument_type_impl(int)
-> decltype(std::is_same<typename T::argument_type, std::tuple<Args...>>{}); // Checking both that T::argument_type exists and that it is the same as std::tuple<Args...>.
template <typename T, typename... Args>
constexpr auto has_argument_type_impl(long) -> std::false_type;
template <typename T, typename... Args>
constexpr bool has_argument_type() { return decltype(has_argument_type_impl<T, Args...>(0))::value; }
template <typename T, std::size_t N, typename... Args>
constexpr auto has_argument_type_n_impl(int)
-> decltype(std::is_same<typename T::template argument_type<N>, std::tuple<Args...>>{}); // Checking both that T::argument_type<N> exists and that it is the same as std::tuple<Args...>.
template <typename T, std::size_t N, typename... Args>
constexpr auto has_argument_type_n_impl(long) -> std::false_type;
template <typename T, std::size_t N, typename... Args>
constexpr bool has_argument_type_n() { return decltype(has_argument_type_n_impl<T, N, Args...>(0))::value; }
template <typename... Ts>
class Factory {
template <std::size_t, typename...> struct rank;
template <std::size_t N, typename First, typename... Rest>
struct rank<N, First, Rest...> : rank<N, Rest...> {};
template <std::size_t N, typename T> struct rank<N,T> : rank<N+1, Ts...> {};
template <typename T> struct rank<10, T> {}; // Need to end the instantiations somewhere.
public:
template <typename... Args>
decltype(auto) create (Args... args) const {
return check(rank<0, Ts...>{}, args...);
}
private:
template <typename T, typename... Rest, typename... Args>
auto check (rank<0, T, Rest...>, Args... args) const
-> std::enable_if_t<has_argument_type<T, Args...>(), decltype(T(args...))> {
return T(args...);
}
template <typename T, typename... Rest, typename... Args>
auto check (rank<0, T, Rest...>, Args... args) const
-> std::enable_if_t<!has_argument_type<T, Args...>(), decltype(check(rank<0, Rest...>{}, args...))> {
return check(rank<0, Rest...>{}, args...);
}
template <typename T, typename... Args>
auto check (rank<0,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T, Args...>(), decltype(check(rank<1, Ts...>{}, args...))> {
return check(rank<1, Ts...>{}, args...); // Since rank<0,T> derives immediately from rank<1, Ts...>.
}
template <std::size_t N, typename T, typename... Rest, typename... Args>
auto check (rank<N, T, Rest...>, Args... args) const
-> std::enable_if_t<has_argument_type_n<T, N-1, Args...>(), decltype(T(args...))> {
return T(args...);
}
template <std::size_t N, typename T, typename... Rest, typename... Args>
auto check (rank<N, T, Rest...>, Args... args) const
-> std::enable_if_t<!has_argument_type_n<T, N-1, Args...>(), decltype(check(rank<N, Rest...>{}, args...))> {
return check(rank<N, Rest...>{}, args...);
}
// I want to use the following instead of what's below it.
// template <std::size_t N, typename T, typename... Args>
// auto check (rank<N,T>, Args... args) const
// -> std::enable_if_t<!has_argument_type_n<T, N-1, Args...>(), decltype(check(rank<N+1, Ts...>{}, args...))> {
// return check(rank<N+1, Ts...>{}, args...); // Since rank<N,T> derives immediately from rank<N+1, Ts...>.
// }
//
// template <typename T, typename... Args>
// auto check (rank<10, T>, Args... args) const { std::cout << "Nothing found.\n"; }
template <typename T, typename... Args>
auto check (rank<1,T>, Args... args) const
-> std::enable_if_t<!has_argument_type_n<T, 0, Args...>(), decltype(check(rank<2, Ts...>{}, args...))> {
return check(rank<2, Ts...>{}, args...); // Since rank<1,T> derives immediately from rank<2, Ts...>.
}
template <typename T, typename... Args>
auto check (rank<2,T>, Args... args) const
-> std::enable_if_t<!has_argument_type_n<T, 1, Args...>(), decltype(check(rank<3, Ts...>{}, args...))> {
return check(rank<3, Ts...>{}, args...); // Since rank<2,T> derives immediately from rank<3, Ts...>.
}
// etc... until rank<9>.
};
// Testing
struct Object {
template <std::size_t, typename = void> struct ArgumentType;
template <typename T> struct ArgumentType<0,T> { using type = std::tuple<int, bool, char, double>; };
template <typename T> struct ArgumentType<1,T> { using type = std::tuple<bool, char, double>; };
template <std::size_t N> using argument_type = typename ArgumentType<N>::type;
Object (int, bool, char, double) { print(); }
Object (bool, char, double) { print(); }
void print() const { std::cout << "Object\n"; }
};
struct Thing {
template <std::size_t, typename = void> struct ArgumentType;
template <typename T> struct ArgumentType<0,T> { using type = std::tuple<int, int, char>; };
template <typename T> struct ArgumentType<1,T> { using type = std::tuple<int, char>; };
template <typename T> struct ArgumentType<2,T> { using type = std::tuple<char>; };
template <std::size_t N> using argument_type = typename ArgumentType<N>::type;
Thing (int, int, char) { print(); }
Thing (int, char) { print(); }
Thing (char) { print(); }
void print() const { std::cout << "Thing\n"; }
};
struct Blob {
using argument_type = std::tuple<int, double>;
Blob (int, double) { print(); }
void print() const { std::cout << "Blob\n"; }
};
struct Widget {
using argument_type = std::tuple<int>;
Widget (double, double, int, double) { print(); }
Widget (int) { print(); }
void print() const { std::cout << "Widget\n"; }
};
int main() {
Factory<Blob, Object, Thing, Widget>().create(4,3.5); // Blob
Factory<Object, Blob, Widget, Thing>().create(2); // Widget
Factory<Object, Thing, Blob, Widget>().create(5); // Widget
Factory<Blob, Object, Thing, Widget>().create(4,true,'a',7.5); // Object
Factory<Blob, Thing, Object, Widget>().create(true,'a',7.5); // Object
Factory<Blob, Object, Thing, Widget>().create('a'); // Thing
}
我知道还有其他方法可以实现这一点,但我试图更好地理解序列排名,并想知道为什么我不能使用已注释掉的部分。如何避免我需要放置的重复代码(到rank<9>
,甚至更高的级别),这些代码目前正在使用此代码?谢谢你的耐心等待。
注意:我实际上不能像现在这样手动输入代码的重复部分。因为在rank<N, Ts...>
重载中使用的check
的最高N值将在编译时确定为最高N值,使得argument_type<N>
成员类型存在于所有Ts...
成员类型中}。因此,我必须使用我注释掉的通用部分,rank<10,T>
我使用的必须将10替换为特定的N值。因此,这不仅仅是为了方便。我必须解决这个问题才能继续开发程序。
编辑:这是一个更小的示例,显示了同样的问题:
#include <iostream>
#include <type_traits>
#include <tuple>
template <typename T>
constexpr auto has_argument_type_impl(int)
-> decltype(typename T::argument_type{}, std::true_type{});
template <typename T>
constexpr auto has_argument_type_impl(long) -> std::false_type;
template <typename T>
constexpr bool has_argument_type() { return decltype(has_argument_type_impl<T>(0))::value; }
template <typename... Ts>
class Factory {
template <std::size_t, typename...> struct rank;
template <std::size_t N, typename First, typename... Rest>
struct rank<N, First, Rest...> : rank<N, Rest...> {};
template <std::size_t N, typename T> struct rank<N,T> : rank<N+1, Ts...> {};
template <typename T> struct rank<10, T> {}; // Need to end the instantiations somewhere.
public:
template <typename... Args>
decltype(auto) create (Args... args) const {
return check(rank<0, Ts...>{}, args...);
}
private:
template <std::size_t N, typename T, typename... Rest, typename... Args>
auto check (rank<N, T, Rest...>, Args... args) const
-> std::enable_if_t<has_argument_type<T>(), decltype(T(args...))> {
return T(args...);
}
template <std::size_t N, typename T, typename... Rest, typename... Args>
auto check (rank<N, T, Rest...>, Args... args) const
-> std::enable_if_t<!has_argument_type<T>(), decltype(check(rank<N, Rest...>{}, args...))> {
return check(rank<N, Rest...>{}, args...);
}
template <typename T, typename... Args>
auto check (rank<0,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T>(), decltype(check(rank<1, Ts...>{}, args...))> {
return check(rank<1, Ts...>{}, args...); // Since rank<0,T> derives immediately from rank<1, Ts...>.
}
// I want to use the following instead of what's below it.
// template <std::size_t N, typename T, typename... Args>
// auto check (rank<N,T>, Args... args) const
// -> std::enable_if_t<!has_argument_type<T>(), decltype(check(rank<N+1, Ts...>{}, args...))> {
// return check(rank<N+1, Ts...>{}, args...); // Since rank<N,T> derives immediately from rank<N+1, Ts...>.
// }
//
// template <typename T, typename... Args>
// auto check (rank<10, T>, Args... args) const { std::cout << "Nothing found.\n"; }
template <typename T, typename... Args>
auto check (rank<1,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T>(), decltype(check(rank<2, Ts...>{}, args...))> {
return check(rank<2, Ts...>{}, args...); // Since rank<1,T> derives immediately from rank<2, Ts...>.
}
template <typename T, typename... Args>
auto check (rank<2,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T>(), decltype(check(rank<3, Ts...>{}, args...))> {
return check(rank<3, Ts...>{}, args...); // Since rank<2,T> derives immediately from rank<3, Ts...>.
}
// etc... until rank<9>.
};
// Testing
struct Object {};
struct Thing {};
struct Blob {
using argument_type = std::tuple<int, double>;
Blob (int, double) { std::cout << "Blob\n"; }
};
int main() {
Factory<Object, Thing, Blob>().create(4,3.5); // Blob
}
答案 0 :(得分:3)
部分排序直到重载解析过程很晚才开始。
忽略各种check
重载中的所有乒乓,最终你最终会
template <std::size_t N, typename T, typename... Args>
auto check (rank<N,T>, Args... args) const
-> std::enable_if_t<!has_argument_type_n<T, N, Args...>(),
decltype(check(rank<N+1, Ts...>{}, args...))>;
template <typename T, typename... Args>
auto check (rank<10, T>, Args... args) const;
rank<10, something I frankly don't care about>
。将对两个重载执行扣除和替换;并且作为替换到第一个签名的返回类型的一部分,您将实例化rank<11, Ts...>
,这反过来绕过rank
的终止特化,从而产生无限的模板实例化链。你甚至没有达到部分订购决胜局选择第二次超载的程度。
将第一次重载限制为N < 10
。它需要在词法上先于返回类型(这样当编译器没有尝试替换它时N >= 10
),所以将它放在默认的模板参数中。