带有可变模板参数的部分特化,后跟其他参数
Partial specialization with variadic template parameters that are followed by other parameters
我对涉及可变模板参数的部分专业化有疑问。带有前缀的专业化
template<typename A, typename ... B>
struct Foo<A, B...> { };
按预期工作,但是当我尝试与后缀匹配时
template<typename A, typename ... B>
struct Foo<B..., A> { };
它不知何故不起作用。是否有一些我不知道的规则或者是编译器问题? (我使用的是 G++ 7.4,特别是来自 cygwin 的 x86_64-w64-mingw32-g++)
演示我的问题的自包含示例:
#include <iostream>
template<char ... C>
struct Str
{
static constexpr char Value[] = { C..., '[=12=]' };
};
template<char ... C>
constexpr char Str<C...>::Value[];
template<typename>
struct TrimFront;
template<char A, char ... C>
struct TrimFront<Str<A, C...>>
{
typedef Str<C...> Type;
};
template<typename>
struct TrimBack;
template<char A, char ... C>
struct TrimBack<Str<C..., A>>
{
typedef Str<C...> Type;
};
int main(int, char **)
{
typedef Str<'a', 'b', 'c', 'd', 'e', 'f'> str_t;
std::cout << str_t::Value << std::endl; // prints "abcdef"
std::cout << TrimFront<str_t>::Type::Value << std::endl; // prints "bcdef"
std::cout << TrimBack<str_t>::Type::Value << std::endl; // ERROR (incomplete type)
return 0;
}
这是一个使用 boost::mp11
的解决方案:
内嵌评论:
#include <iostream>
#include <boost/mp11.hpp>
template<char ... C>
struct Str
{
static constexpr char Value[] = { C..., '[=10=]' };
};
template<char ... C>
constexpr char Str<C...>::Value[];
template<typename>
struct TrimFront;
template<char A, char ... C>
struct TrimFront<Str<A, C...>>
{
typedef Str<C...> Type;
};
template<typename>
struct TrimBack;
using namespace boost::mp11;
// a means of turning chars into types
template<char c> struct c_char {
constexpr char value() { return c; }
};
// a means of turning an mp_list of c_char<char>... back into a Str<char...>
template<typename>
struct back_to_Str;
template<char...cs>
struct back_to_Str<mp_list<c_char<cs>...>>
{
using result = Str<cs...>;
};
// TrimBack using types as computation steps:
template<char... C>
struct TrimBack<Str<C...>>
{
// turn the input chars into an mp_list of c_char
// always use types, they're much easier than values when metaprogramming
using input = mp_list<c_char<C>...>;
// reverse the list
using reversed = mp_reverse<input>;
// pop the front c_char<>
using popped = mp_pop_front<reversed>;
// reverse again
using re_reversed = mp_reverse<popped>;
// turn back into a Str<char...>
using Type = typename back_to_Str<re_reversed>::result;
};
int main(int, char **)
{
typedef Str<'a', 'b', 'c', 'd', 'e', 'f'> str_t;
std::cout << str_t::Value << std::endl; // prints "abcdef"
std::cout << TrimFront<str_t>::Type::Value << std::endl; // prints "bcdef"
std::cout << TrimBack<str_t>::Type::Value << std::endl; // prints "abcde"
return 0;
}
预期输出:
abcdef
bcdef
abcde
http://coliru.stacked-crooked.com/a/387e5dc7ef262f1f
利用我们新发现的知识,我们可以简化:
#include <iostream>
#include <boost/mp11.hpp>
using namespace boost::mp11;
template<char c>
struct c_char {
static constexpr char value() { return c; }
};
template<typename...> struct Str;
template<char... C>
struct Str<c_char<C>...>
{
static constexpr auto size() -> std::size_t { return sizeof...(C) + 1; }
static constexpr char Value [size()] = { C..., '[=12=]' };
};
template<char...C> using make_Str = Str<c_char<C>...>;
template<typename List>
struct TrimFront
{
using Type = mp_pop_front<List>;
};
template<typename List>
struct TrimBack
{
using Type = mp_reverse<mp_pop_front<mp_reverse<List>>>;
};
int main(int, char **)
{
using str_t = make_Str<'a', 'b', 'c', 'd', 'e', 'f'>;
std::cout << str_t::Value << std::endl; // prints "abcdef"
std::cout << TrimFront<str_t>::Type::Value << std::endl; // prints "bcdef"
std::cout << TrimBack<str_t>::Type::Value << std::endl; // prints "abcde"
return 0;
}
我想
template<char A, char ... C>
struct TrimBack<Str<C..., A>>
{
typedef Str<C...> Type;
};
无法工作(“A
”和“C...
”)无法推断,因为可变参数包(C...
)不在最后位置。
OP,有理有据,求参考
really? very unfortunate. can you by chance point to where it says so in the standard? i cannot seem to find the relevant part
我不是语言层,但在我看来相关部分(C++11 标准)是 14.8.2.5("Deducing template argument from a type"、“[temp.deduct.type]”),第 9 点(强调我的)
If P
has a form that contains <T>
or <i>
, then each argument P_i
of the respective template argument list P
is compared with the corresponding argument A_i
of the corresponding template argument list of A
. If the template argument list of P
contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context. If P_i
is a pack expansion, then the pattern of P_i
is compared with each remaining argument in the template argument list of A
. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by P_i
.
所以,如果我没记错的话,TrimBack<str_t>
(a.k.a. TrimBack<Str<'a', 'b', 'c', 'd', 'e', 'f'>>
) 会给出错误,因为
1) 在第一阶段,Str<C..., A>
匹配 Str<'a', 'b', 'c', 'd', 'e', 'f'>
2) 但在第二阶段,试图推导 C...
和 A
类型, P
(即 Str<C..., A>
,在这个阶段) "contains a pack expansion that is not the last template argument", 所以 "the entire template argument list is a non-deduced context".
像这样的部分 class 模板专业化
template<typename> struct TrimBack;
template<char ...C, char A> struct TrimBack<Str<C..., A>> {}
是不允许的,因为要推导出C...
和A
,deduction from a type is performed, and a pack argument that is not last makes it a non-deduced context.
你可以做的是使用辅助类型 "unwrap" 包,然后 "re-wrap" 它,减去最后一个元素。
template <char ...P>
struct dummy {};
template <class T, char ...P>
struct internal;
template <char ...P1, char T, char ...P2>
struct internal<dummy<P1...>, T, P2...>
{
using type = typename internal<dummy<P1..., T>, P2...>::type; // unwrap one recursively
};
template <char ...P1, char T>
struct internal<dummy<P1...>, T>
{
using type = Str<P1...>; // re-wrap all but the last one
};
template <typename>
struct TrimBack;
template <char ...C>
struct TrimBack<Str<C...>>
{
using Type = typename internal<dummy<>, C...>::type;
};
现在应该可以了:
std::cout << TrimBack<str_t>::Type::Value << std::endl; // prints "abcde"
我对涉及可变模板参数的部分专业化有疑问。带有前缀的专业化
template<typename A, typename ... B>
struct Foo<A, B...> { };
按预期工作,但是当我尝试与后缀匹配时
template<typename A, typename ... B>
struct Foo<B..., A> { };
它不知何故不起作用。是否有一些我不知道的规则或者是编译器问题? (我使用的是 G++ 7.4,特别是来自 cygwin 的 x86_64-w64-mingw32-g++)
演示我的问题的自包含示例:
#include <iostream>
template<char ... C>
struct Str
{
static constexpr char Value[] = { C..., '[=12=]' };
};
template<char ... C>
constexpr char Str<C...>::Value[];
template<typename>
struct TrimFront;
template<char A, char ... C>
struct TrimFront<Str<A, C...>>
{
typedef Str<C...> Type;
};
template<typename>
struct TrimBack;
template<char A, char ... C>
struct TrimBack<Str<C..., A>>
{
typedef Str<C...> Type;
};
int main(int, char **)
{
typedef Str<'a', 'b', 'c', 'd', 'e', 'f'> str_t;
std::cout << str_t::Value << std::endl; // prints "abcdef"
std::cout << TrimFront<str_t>::Type::Value << std::endl; // prints "bcdef"
std::cout << TrimBack<str_t>::Type::Value << std::endl; // ERROR (incomplete type)
return 0;
}
这是一个使用 boost::mp11
的解决方案:
内嵌评论:
#include <iostream>
#include <boost/mp11.hpp>
template<char ... C>
struct Str
{
static constexpr char Value[] = { C..., '[=10=]' };
};
template<char ... C>
constexpr char Str<C...>::Value[];
template<typename>
struct TrimFront;
template<char A, char ... C>
struct TrimFront<Str<A, C...>>
{
typedef Str<C...> Type;
};
template<typename>
struct TrimBack;
using namespace boost::mp11;
// a means of turning chars into types
template<char c> struct c_char {
constexpr char value() { return c; }
};
// a means of turning an mp_list of c_char<char>... back into a Str<char...>
template<typename>
struct back_to_Str;
template<char...cs>
struct back_to_Str<mp_list<c_char<cs>...>>
{
using result = Str<cs...>;
};
// TrimBack using types as computation steps:
template<char... C>
struct TrimBack<Str<C...>>
{
// turn the input chars into an mp_list of c_char
// always use types, they're much easier than values when metaprogramming
using input = mp_list<c_char<C>...>;
// reverse the list
using reversed = mp_reverse<input>;
// pop the front c_char<>
using popped = mp_pop_front<reversed>;
// reverse again
using re_reversed = mp_reverse<popped>;
// turn back into a Str<char...>
using Type = typename back_to_Str<re_reversed>::result;
};
int main(int, char **)
{
typedef Str<'a', 'b', 'c', 'd', 'e', 'f'> str_t;
std::cout << str_t::Value << std::endl; // prints "abcdef"
std::cout << TrimFront<str_t>::Type::Value << std::endl; // prints "bcdef"
std::cout << TrimBack<str_t>::Type::Value << std::endl; // prints "abcde"
return 0;
}
预期输出:
abcdef
bcdef
abcde
http://coliru.stacked-crooked.com/a/387e5dc7ef262f1f
利用我们新发现的知识,我们可以简化:
#include <iostream>
#include <boost/mp11.hpp>
using namespace boost::mp11;
template<char c>
struct c_char {
static constexpr char value() { return c; }
};
template<typename...> struct Str;
template<char... C>
struct Str<c_char<C>...>
{
static constexpr auto size() -> std::size_t { return sizeof...(C) + 1; }
static constexpr char Value [size()] = { C..., '[=12=]' };
};
template<char...C> using make_Str = Str<c_char<C>...>;
template<typename List>
struct TrimFront
{
using Type = mp_pop_front<List>;
};
template<typename List>
struct TrimBack
{
using Type = mp_reverse<mp_pop_front<mp_reverse<List>>>;
};
int main(int, char **)
{
using str_t = make_Str<'a', 'b', 'c', 'd', 'e', 'f'>;
std::cout << str_t::Value << std::endl; // prints "abcdef"
std::cout << TrimFront<str_t>::Type::Value << std::endl; // prints "bcdef"
std::cout << TrimBack<str_t>::Type::Value << std::endl; // prints "abcde"
return 0;
}
我想
template<char A, char ... C>
struct TrimBack<Str<C..., A>>
{
typedef Str<C...> Type;
};
无法工作(“A
”和“C...
”)无法推断,因为可变参数包(C...
)不在最后位置。
OP,有理有据,求参考
really? very unfortunate. can you by chance point to where it says so in the standard? i cannot seem to find the relevant part
我不是语言层,但在我看来相关部分(C++11 标准)是 14.8.2.5("Deducing template argument from a type"、“[temp.deduct.type]”),第 9 点(强调我的)
If
P
has a form that contains<T>
or<i>
, then each argumentP_i
of the respective template argument listP
is compared with the corresponding argumentA_i
of the corresponding template argument list ofA
. If the template argument list ofP
contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context. IfP_i
is a pack expansion, then the pattern ofP_i
is compared with each remaining argument in the template argument list ofA
. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded byP_i
.
所以,如果我没记错的话,TrimBack<str_t>
(a.k.a. TrimBack<Str<'a', 'b', 'c', 'd', 'e', 'f'>>
) 会给出错误,因为
1) 在第一阶段,Str<C..., A>
匹配 Str<'a', 'b', 'c', 'd', 'e', 'f'>
2) 但在第二阶段,试图推导 C...
和 A
类型, P
(即 Str<C..., A>
,在这个阶段) "contains a pack expansion that is not the last template argument", 所以 "the entire template argument list is a non-deduced context".
像这样的部分 class 模板专业化
template<typename> struct TrimBack;
template<char ...C, char A> struct TrimBack<Str<C..., A>> {}
是不允许的,因为要推导出C...
和A
,deduction from a type is performed, and a pack argument that is not last makes it a non-deduced context.
你可以做的是使用辅助类型 "unwrap" 包,然后 "re-wrap" 它,减去最后一个元素。
template <char ...P>
struct dummy {};
template <class T, char ...P>
struct internal;
template <char ...P1, char T, char ...P2>
struct internal<dummy<P1...>, T, P2...>
{
using type = typename internal<dummy<P1..., T>, P2...>::type; // unwrap one recursively
};
template <char ...P1, char T>
struct internal<dummy<P1...>, T>
{
using type = Str<P1...>; // re-wrap all but the last one
};
template <typename>
struct TrimBack;
template <char ...C>
struct TrimBack<Str<C...>>
{
using Type = typename internal<dummy<>, C...>::type;
};
现在应该可以了:
std::cout << TrimBack<str_t>::Type::Value << std::endl; // prints "abcde"