我想知道为什么以下代码无法编译:
struct S
{
template <typename... T>
S(T..., int);
};
S c{0, 0};
此代码无法使用clang和GCC 4.8进行编译。这是clang的错误:
test.cpp:7:3: error: no matching constructor for initialization of 'S'
S c{0, 0};
^~~~~~~
test.cpp:4:5: note: candidate constructor not viable: requires 1 argument, but 2 were provided
S(T..., int);
^
在我看来,这应该有效,并且T应该被推断为长度为1的包。
如果标准禁止做这样的事情,有人知道为什么吗?
答案 0 :(得分:9)
因为当函数参数包不是最后一个参数时,模板参数包不能从它推导出来,并且模板参数推导将忽略它。
因此,将0, 0
两个参数与, int
进行比较,产生不匹配。
这样的扣除规则需要涵盖许多特殊情况(例如当两个参数包彼此相邻时会发生什么)。由于参数包是C ++ 11中的新功能,因此相应提案的作者保守地起草了规则。
请注意,如果没有以其他方式推断,则尾随模板参数包将为空。事情当你用一个参数调用构造函数时,事情就会起作用(注意模板参数包和函数参数包的不同之处。前者是尾随的,后者不是)。
答案 1 :(得分:6)
所以,应该有一个解决方法。这些方面的东西:
namespace v1 {
// Extract the last type in a parameter pack.
// 0, the empty pack has no last type (only called if 1 and 2+ don't match)
template<typename... Ts>
struct last_type {};
// 2+ in pack, recurse:
template<typename T0, typename T1, typename... Ts>
struct last_type<T0, T1, Ts...>:last_type<T1, Ts...>{};
// Length 1, last type is only type:
template<typename T0>
struct last_type<T0> {
typedef T0 type;
};
}
namespace v2 {
template<class T> struct tag_t{using type=T;};
template<class T> using type_t = typename T::type;
template<class...Ts>
using last = type_t< std::tuple_element_t< sizeof...(Ts)-1, std::tuple<tag_t<Ts>...> > >;
template<class...Ts>
struct last_type {
using type=last<Ts...>;
};
}
template<class...Ts>
using last_type=v2::late_type<Ts...>; // or v1
struct S
{
// We accept any number of arguments
// So long as the type of the last argument is an int
// probably needs some std::decay to work right (ie, to implicitly work out that
// the last argument is an int, and not a const int& or whatever)
template <typename... T, typename=typename std::enable_if<std::is_same<int, typename last_type<T...>::type>>::type>
S(T...);
};
我们检查参数包的最后一个类型是int
,还是我们只传递了int
。
答案 2 :(得分:2)
从标准N3376§14.1的工作草案中可以看到一个可能的部分。
以下是§14.1.11
如果类模板或别名模板的模板参数具有 default template-argument,每个后续的模板参数都应该 要么提供了默认的模板参数,要么是模板 参数包。如果是主类模板的模板参数或 别名模板是模板参数包,它应该是最后一个 模板参数。功能模板的模板参数包 除非另有说明,否则不得跟随另一个模板参数 模板参数可以从参数类型列表中推导出来 函数模板或具有默认参数。
答案 3 :(得分:1)
我实际上对同一件事感兴趣(希望根据最终参数专门化模板化参数包)。
我相信通过将元组反转(std::make_tuple
,后端口std::apply
与C ++ 14等结合起来,可能会有一条前进道路:
如果成功,将重新回到这里。
相关帖子:
修改强>: 是的,稍微想了一下;不完美,因为有额外的副本飞来飞去,但这是一个开始。
如果您知道比我下面列出的更简单的方式,请不要犹豫发布!
可以做这样的事情:
auto my_func_callable = [] (auto&& ... args) {
return my_func(std::forward<decltype(args)>(args)...);
};
auto my_func_reversed =
stdcustom::make_callable_reversed(my_func_callable);
然后实现这个pseduo代码:
template<typename ... Args>
void my_func(Args&& ... args, const my_special_types& x);
做类似的事情:
template<typename... Args>
void my_func(Args&& ... args)
-> call my_func_reversed(args...)
template<typename... RevArgs>
void my_func_reversed(const my_special_types& x, RevArgs&&... revargs)
-> do separate things with revargs and my_special_types
-> sub_func_reversed(revargs...)
使用上述实用程序。
有一些(很多)缺点。将在下面列出。
这适用于想要借用未来的C ++ 14(可能是C ++ 11)的用户(C ++ 17)。
有几种不同的方法可以做到这一点。我在这个例子中列出了一些替代方案:
std::apply_impl
传递的索引(credit:Orient)。index_sequence
(credit:Xeo)tuple.output.txt - 示例输出
这会从Xeo的示例中打印出reversed_index_sequence
模板。我需要这个来进行调试。
>>> name_trait<std::make_index_sequence<5>>::name()
std::index_sequence<0, 1, 2, 3, 4>
>>> name_trait<make_reversed_index_sequence<5>>::name()
std::index_sequence<4, 3, 2, 1, 0>
我选择了备选方案1,因为它更容易消化。 然后我试着快速正式化它:
namespace stdfuture
),并进行扩展(namespace stdcustom
)定义片段(适应C ++ 17可能在cppreference.com上实现std::apply
):
namespace detail {
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_reversed_impl(F &&f,
Tuple &&t, std::index_sequence<I...>)
{
// @ref https://stackoverflow.com/a/31044718/7829525
// Credit: Orient
constexpr std::size_t back_index = sizeof...(I) - 1;
return f(std::get<back_index - I>(std::forward<Tuple>(t))...);
}
} // namespace detail
template <class F, class Tuple>
constexpr decltype(auto) apply_reversed(F &&f, Tuple &&t)
{
// Pass sequence by value to permit template inference
// to parse indices as parameter pack
return detail::apply_reversed_impl(
std::forward<F>(f), std::forward<Tuple>(t),
std::make_index_sequence<
std::tuple_size<std::decay_t<Tuple>>::value>{});
}
使用代码段:(来自tuple_future_main.output.txt
,从上面复制)
auto my_func_callable = [] (auto&& ... args) {
return my_func(std::forward<decltype(args)>(args)...);
};
auto my_func_reversed =
stdcustom::make_callable_reversed(my_func_callable);
首先,为您希望使用的最终参数建立模式。您必须明确枚举这些,因为您只能有一个参数包。
(取自tuple_future_main.cc):
示例场景:
我们希望将容器添加到具有名称的容器中,其形式为:
add_item(const Item& item, const string& name, Container& c)
我们还可以构造一个具有[非常大]重载次数的项目,以及 我们有方便的超载:
add_item(${ITEM_CTOR_ARGS}, const string& name, Container& c)
为此,我们可以声明以下内容:
void add_item_direct(const Item& item, const string& name, Container& c)
Item create_item(Args&&... args)
然后定义我们的通用接口:
template<typename... Args>
void add_item(Args&&... args) {
...
auto reversed = stdcustom::make_callable_reversed(callable);
reversed(std::forward<Args>(args)...);
}
template<typename ... RevArgs>
void add_item_reversed(Container& c, const string& name, RevArgs&&... revargs)
{
...
static auto ctor = VARIADIC_CALLABLE(create_item,);
...
auto item = ctor_reversed(std::forward<RevArgs>(revargs)...);
add_item_direct(item, name, c);
}
现在我们可以执行以下操作:(取自tuple_future_main.output.txt
)
>>> (add_item(Item("attribute", 12), "bob", c));
>>> (add_item("attribute", 12, "bob", c));
>>> (add_item(Item(2, 2.5, "twelve"), "george", c));
>>> (add_item(2, 2.5, "twelve", "george", c));
>>> (add_item(Item(2, 15.), "again", c));
>>> (add_item(2, 15., "again", c));
>>> c
bob - ctor3: ctor3: ctor1: attribute (12, 10)
bob - ctor3: ctor1: attribute (12, 10)
george - ctor3: ctor3: ctor2: 2, 2.5 (twelve)
george - ctor3: ctor2: 2, 2.5 (twelve)
again - ctor3: ctor3: ctor2: 2, 15 ()
again - ctor3: ctor2: 2, 15 ()
注意额外的副本构造函数...... :(
make_reversed_index_sequence
并直接发送给该功能(在其他SO帖子中提及)。但重复这件事很痛苦。Callable
尝试打击参数包贪婪
是否存在与lvalue-和rvalue-references匹配的通用std::enable_if
匹配,并且可能处理转发兼容的隐式复制构造函数?
template<typename ... Args>
void my_func(Args&& ... args) // Greedy
void my_func(magical_ref_match<string>::type, ...)
// If this could somehow automatically snatch `const T&` and `T&&` from the parameter pack...
// And if it can be used flexible with multiple arguments, combinatorically