可变参数模板的未声明标识符

Undeclared identifier for Variadic Template

本文关键字:未声明 标识符 变参 参数      更新时间:2023-10-16

我还在学习如何使用可变参数模板。基本上我想做的是采用一个包含类型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的答案,因为它似乎是最"通用证明"的解决方案,并提供了对我的代码的见解。但是,我想说@Walter的答案同样不错。虽然@max66的答案是解决问题的最简单答案,但最初的问题是我试图错误地描述 STL 容器。

尝试用这种方式编写泛型代码注定会失败,因为在一般情况下容器不能用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;
}

怎么样

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)

但是,让我们看一下代码的含义:

  1. 类型STLContainer必须采用模板参数。如果我写一个类simplestringvector(我知道这不是通用的,但没有人能阻止我:)),你的代码对我不起作用。

  2. STLContainer类型必须提供begin()end()成员。(没有免费功能;没有std::beginstd::end)

  3. String类型必须提供pop_back()成员。

  4. String类型必须定义operator+=,它必须能够处理包含char的字符串文字(无wchar_t,无char16_t,...)。

  5. 还有一些在这里不那么成问题的地方。

该函数可以更通用:

没有保修,因为我很累,但无论如何...

如果你的代码要求类型是可迭代的,你首先不需要知道 String 的类型。您可以将其作为取消引用容器迭代器的衰减结果。通过将std::beginstd::end拖到作用域中,可以为免费的开始和结束函数启用 ADL,同时仍通过 std 函数捕获成员函数。

首先是一些标头和一个帮助程序类:

#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','; };
}

现在,我们尝试使用启用 ADL 的迭代来实现as_comma_list,并且对容器或字符串的模板布局没有任何约束。

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 类型也是可迭代的(这很常见),并且在循环中具有第二个分支,在这里处理数十亿个字符串时可能会更慢。