可变参数模板的未声明标识符
Undeclared identifier for Variadic Template
我仍在学习如何使用可变参数模板。基本上我想要做的是采用包含 String
类型元素的 STLContainer
。 STL 容器不采用固定数量的参数,因此我尝试使用可变参数模板。如果我理解正确的话,我应该可以这样写:
/* Take a container of String and return a string of those elements separated by commas.*/
template < template <typename String, typename ... Traits > class STLContainer >
String as_comma_list(const STLContainer<String, Traits> &container)
{
String result;
for (auto it = container.begin(); it != container.end(); it++)
{
result += *it;
result += ",";
}
result.pop_back(); // pop last comma
return result;
}
但是编译器 (Apple LLVM version 8.1.0
) 吐出:
error: use of undeclared identifier 'Traits'
非常感谢任何帮助。
编辑:我最终选择了@Pixelchemist 的答案,因为它似乎是最 "generic proof" 的解决方案,并提供了对我的代码的洞察力。但是,我想说@Walter 的回答同样好。虽然@max66 的回答是最简单的解决问题的方法,但最初的问题是我试图错误地描述一个 STL 容器。
那
呢
template <template <typename...> class STLContainer,
typename String, typename Traits>
String as_comma_list(const STLContainer<String, Traits> &container)
?
但是你需要Traits
?
我想你可以简化你的代码如下
template <template <typename...> class STLContainer, typename String>
String as_comma_list(const STLContainer<String> &container)
您的代码必须如下所示:
template < template <class...> class STLContainer, class String, class ...Traits>
String as_comma_list(const STLContainer<String, Traits...> &container)
但让我们看看您的代码的含义:
类型STLContainer
必须采用模板参数。如果我写一个 class simplestringvector
(我知道这不是通用的,但没有人能阻止我 :))你的代码对我不起作用。
STLContainer
类型必须提供 begin()
和 end()
成员。 (没有自由函数;没有 std::begin
和 std::end
)
String
类型必须提供一个 pop_back()
成员。
String
类型必须有 operator+=
定义,它必须能够处理包含 char
的字符串文字(没有 wchar_t
,没有char16_t
, ...).
以及其他在这里不是问题的问题。
函数可以更通用:
没有保修,因为我很累,但无论如何...
如果您的代码无论如何都要求类型是可迭代的,那么您不需要首先知道 String 的类型。您可以将其作为取消引用容器迭代器的衰减结果来获取。通过将 std::begin
和 std::end
拖到作用域中,您可以启用 ADL 免费开始和结束函数,同时仍然通过标准函数捕获成员函数。
首先是一些 headers 和一个帮手 class:
#include <type_traits>
#include <iterator>
namespace detail
{
template<class ...> struct comma;
template<> struct comma<char>
{ static constexpr char c = ','; };
template<> struct comma<wchar_t>
{ static constexpr wchar_t c = L','; };
template<> struct comma<char16_t>
{ static constexpr char16_t c = u','; };
template<> struct comma<char32_t>
{ static constexpr char16_t c = U','; };
}
现在我们尝试实现 as_comma_list
启用 ADL 的迭代,并且对容器或字符串的模板布局没有任何限制。
template <class C>
auto as_comma_list(C&& c)
{
using std::begin;
using std::end;
using string_type = std::decay_t<decltype(*begin(c))>;
using char_type = std::decay_t<decltype(*begin(*begin(c)))>;
string_type result;
auto const ec = end(c);
for (auto it = begin(c); it != ec; )
{
result += *it;
if (++it != ec)
{
result += detail::comma<char_type>::c;
}
else break;
}
return result;
}
注意:此示例要求 String 类型也是可迭代的(这很常见)并且它在循环中有第二个分支,在此处处理数十亿个字符串时可能会更慢。
尝试用这种方式编写通用代码注定会失败,因为一般容器不能描述为container<T, traits...>
,想想map<key, T, Compare, Allocator>
,其中有value_type=pair<key, T>
.
相反,在 C++ 中,这种类型的泛型编程通常是通过 iterators
完成的(就像整个标准库一样),例如
template<typename It>
enable_if_t<is_same<string, typename iterator_traits<It>::value_type>::value,
string> // or use static_assert() in the function body
as_comma_list(It begin, const It &end)
{
string result;
for(; begin!=end; ++begin)
{
result += *begin;
result += ",";
}
result.pop_back();
return result;
}
我仍在学习如何使用可变参数模板。基本上我想要做的是采用包含 String
类型元素的 STLContainer
。 STL 容器不采用固定数量的参数,因此我尝试使用可变参数模板。如果我理解正确的话,我应该可以这样写:
/* Take a container of String and return a string of those elements separated by commas.*/
template < template <typename String, typename ... Traits > class STLContainer >
String as_comma_list(const STLContainer<String, Traits> &container)
{
String result;
for (auto it = container.begin(); it != container.end(); it++)
{
result += *it;
result += ",";
}
result.pop_back(); // pop last comma
return result;
}
但是编译器 (Apple LLVM version 8.1.0
) 吐出:
error: use of undeclared identifier 'Traits'
非常感谢任何帮助。
编辑:我最终选择了@Pixelchemist 的答案,因为它似乎是最 "generic proof" 的解决方案,并提供了对我的代码的洞察力。但是,我想说@Walter 的回答同样好。虽然@max66 的回答是最简单的解决问题的方法,但最初的问题是我试图错误地描述一个 STL 容器。
那
呢template <template <typename...> class STLContainer,
typename String, typename Traits>
String as_comma_list(const STLContainer<String, Traits> &container)
?
但是你需要Traits
?
我想你可以简化你的代码如下
template <template <typename...> class STLContainer, typename String>
String as_comma_list(const STLContainer<String> &container)
您的代码必须如下所示:
template < template <class...> class STLContainer, class String, class ...Traits>
String as_comma_list(const STLContainer<String, Traits...> &container)
但让我们看看您的代码的含义:
类型
STLContainer
必须采用模板参数。如果我写一个 classsimplestringvector
(我知道这不是通用的,但没有人能阻止我 :))你的代码对我不起作用。STLContainer
类型必须提供begin()
和end()
成员。 (没有自由函数;没有std::begin
和std::end
)String
类型必须提供一个pop_back()
成员。String
类型必须有operator+=
定义,它必须能够处理包含char
的字符串文字(没有wchar_t
,没有char16_t
, ...).以及其他在这里不是问题的问题。
函数可以更通用:
没有保修,因为我很累,但无论如何...
如果您的代码无论如何都要求类型是可迭代的,那么您不需要首先知道 String 的类型。您可以将其作为取消引用容器迭代器的衰减结果来获取。通过将 std::begin
和 std::end
拖到作用域中,您可以启用 ADL 免费开始和结束函数,同时仍然通过标准函数捕获成员函数。
首先是一些 headers 和一个帮手 class:
#include <type_traits>
#include <iterator>
namespace detail
{
template<class ...> struct comma;
template<> struct comma<char>
{ static constexpr char c = ','; };
template<> struct comma<wchar_t>
{ static constexpr wchar_t c = L','; };
template<> struct comma<char16_t>
{ static constexpr char16_t c = u','; };
template<> struct comma<char32_t>
{ static constexpr char16_t c = U','; };
}
现在我们尝试实现 as_comma_list
启用 ADL 的迭代,并且对容器或字符串的模板布局没有任何限制。
template <class C>
auto as_comma_list(C&& c)
{
using std::begin;
using std::end;
using string_type = std::decay_t<decltype(*begin(c))>;
using char_type = std::decay_t<decltype(*begin(*begin(c)))>;
string_type result;
auto const ec = end(c);
for (auto it = begin(c); it != ec; )
{
result += *it;
if (++it != ec)
{
result += detail::comma<char_type>::c;
}
else break;
}
return result;
}
注意:此示例要求 String 类型也是可迭代的(这很常见)并且它在循环中有第二个分支,在此处处理数十亿个字符串时可能会更慢。
尝试用这种方式编写通用代码注定会失败,因为一般容器不能描述为container<T, traits...>
,想想map<key, T, Compare, Allocator>
,其中有value_type=pair<key, T>
.
相反,在 C++ 中,这种类型的泛型编程通常是通过 iterators
完成的(就像整个标准库一样),例如
template<typename It>
enable_if_t<is_same<string, typename iterator_traits<It>::value_type>::value,
string> // or use static_assert() in the function body
as_comma_list(It begin, const It &end)
{
string result;
for(; begin!=end; ++begin)
{
result += *begin;
result += ",";
}
result.pop_back();
return result;
}