test_container
是一个仅用于“调试”目的的函数。对我的工作项目没有真正的用处。
真正的函数称为
foreach
(参见本问题的最后一个代码)
foreach
是一个返回容器的 std::string 表示形式的函数(在此修复之后,我将使其也适用于 int 数组)
甚至这里的
foreach
表示也被最小化,以便更好地理解:它在这里看起来像 test_container
,但有一些差异。
所以,我有这个代码:
#include <iostream>
#include <map>
#include <sstream>
#include <type_traits>
#include <vector>
template <typename T>
struct has_template_argument {
static const bool value = false;
};
// Specialization of a template for types with template arguments
template <template <typename...> class Container, typename T>
struct has_template_argument<Container<T>> {
static const bool value =
!std::is_same<std::basic_string<T>, Container<T>>::value;
};
// Function to verify if the variable has a template argument
template <typename T>
bool has_template_arg(const T&) {
return has_template_argument<T>::value;
}
// The test collection funtion.
template <typename T>
std::string test_container(const T& container) {
std::stringstream listn;
listn << "{";
if (has_template_arg(container)) {
auto it = container.begin();
if (it != container.end()) {
if (!has_template_arg(*it)) {
listn << *it;
std::cout << "\n";
} else {
// It's here that the problem starts.
listn << test_container(*it);
std::cout << "\nThis container is multi-dimentioned";
}
++it;
}
} else {
std::cout << "\nThis variable has no container to \"iterate\"";
}
listn << "}";
return listn.str();
}
说明:
test_container
函数首先尝试查看传递的参数是否具有模板参数。
为此,它使用
has_template_arg
函数。 has_template_arg
对于所有具有 <> 的内容都返回 True,除了多个模板参数(目前)和字符串(预期,因为字符串不应该被视为容器)
函数中的
std::cout
仅用于证明该函数通常属于某种条件 ( if (!has_template_arg(*it))
);然而,当对当前函数进行递归调用时,它会忽略条件并错误地执行递归。
这证明问题显然不在
has_template_arg
检查器中:
std::map<int, int> mep = {{1, 2}, {4, 5}};
std::vector<std::vector<int>> vet = {{1, 2, 3}, {4, 5, 6}};
std::vector<std::string> strVet = {"hello", "world"};
std::string str = "Hi";
const char* chr = "Some char here";
// Works perfectly:
std::cout << "Is Vector container?: " << has_template_arg(strVet) << std::endl;
std::cout << "Is Multivector container?: " << has_template_arg(vet) << std::endl;
// This below CURRENTLY does not work: No problem for now, can wait.
std::cout << "Is Map container?: " << has_template_arg(mep) << std::endl;
// Also works:
std::cout << "Is Int container?: " << has_template_arg(5) << std::endl;
std::cout << "Is Char container?: " << has_template_arg('o') << std::endl;
std::cout << "Is Char[] container?: " << has_template_arg("oi") << std::endl;
std::cout << "Is Const char container?: " << has_template_arg(chr) << std::endl;
std::cout << "Is String container: " << has_template_arg(str) << std::endl;
std::cout << "Vector Recursion: " << test_container(strVet) << std::endl;
// std::cout << "String Recursion: " << test_container(str) << std::endl;
// std::cout << "Multivector recursion: " << foreach(vet) << std::endl;
// std::cout << "Const char recursion: " << test_container(chr) << std::endl;
重要提示:如果注释掉代码将进入递归的行:
// listn << test_container(*it);
一切都会正常工作。
但是,它将无法根据需要检查传递的参数是否也应该“迭代”或应该发送到字符串流。
另一方面,如果不(评论),则会出现以下编译错误:
std::cout << "Vector Recursion: " << test_container(strVet) << std::endl;
// std::cout << "String Recursion: " << test_container(str) << std::endl;
// std::cout << "Multivector recursion: " << foreach(vet) << std::endl;
// std::cout << "Const char recursion: " << foreach(chr) << std::endl;
这是结果:
Compiling single file...
--------
- Filename: C:\Users\user\Workstation\C\CPP-Projects\TesteCPP\Testes\ephemeral.cpp
- Compiler Name: TDM-GCC 4.9.2 64-bit Release
Processing C++ source file...
--------
- C++ Compiler: C:\Program Files (x86)\Dev-Cpp\MinGW64\bin\g++.exe
- Command: g++.exe [...]
C:\Users\user\Workstation\C\CPP-Projects\TesteCPP\Testes\ephemeral.cpp: In instantiation of 'std::string test_container(const T&) [with T = char; std::string = std::basic_string<char>]':
[...]\ephemeral.cpp:42:41: recursively required from 'std::string test_container(const T&) [with T = std::basic_string<char>; std::string = std::basic_string<char>]'
[...]\ephemeral.cpp:42:41: required from 'std::string test_container(const T&) [with T = std::vector<std::basic_string<char> >; std::string = std::basic_string<char>]'
[...]\ephemeral.cpp:100:60: required from here
[...]\ephemeral.cpp:35:29: error: request for member 'begin' in 'container', which is of non-class type 'const char'
auto it = container.begin();
^
[...]\ephemeral.cpp:36:10: error: request for member 'end' in 'container', which is of non-class type 'const char'
if (it != container.end()) {
^
// std::cout << "Vector Recursion: " << test_container(strVet) << std::endl;
std::cout << "String Recursion: " << test_container(str) << std::endl;
// std::cout << "Multivector recursion: " << foreach(vet) << std::endl;
// std::cout << "Const char recursion: " << test_container(chr) << std::endl;
这就是你得到的:
Compiling single file...
--------
- Filename: C:\Users\user\Workstation\C\CPP-Projects\TesteCPP\Testes\ephemeral.cpp
- Compiler Name: TDM-GCC 4.9.2 64-bit Release
Processing C++ source file...
--------
[...]
[...]\ephemeral.cpp: In instantiation of 'std::string test_container(const T&) [with T = char; std::string = std::basic_string<char>]':
[...]\ephemeral.cpp:41:40: required from 'std::string test_container(const T&) [with T = std::basic_string<char>; std::string = std::basic_string<char>]'
[...]\ephemeral.cpp:100:51: required from here
[...]\ephemeral.cpp:35:29: error: request for member 'begin' in 'container', which is of non-class type 'const char'
auto it = container.begin();
^
C:\Users\user\Workstation\C\CPP-Projects\TesteCPP\Testes\ephemeral.cpp:36:10: error: request for member 'end' in 'container', which is of non-class type 'const char'
if (it != container.end()) {
^
这是
foreach
:
// This is the "real" code I said (that also needs fix, although it works):
template <typename T>
std::string foreach(const T& collection) {
std::stringstream listn;
listn << "{";
auto it = collection.begin();
if (it != collection.end()) {
if (has_template_arg(*it)) {
// listn << foreach(*it);
} else {
listn << *it;
}
++it;
}
while (it != collection.end()) {
listn << "," << *it;
++it;
}
listn << "}";
return listn.str();
}
我尝试过多个版本的 GNU C++ 编译器,它们都返回相同的错误消息,尽管存在一些差异。
这意味着,这是一个简单的语法错误 - 已知它从哪里开始,但不知道如何修复它。
预期: GCC g++ 编译代码,没有错误或警告。这意味着需要 test_container
函数知道在其中进行递归的时间。
由于您使用的是C++11,因此您无法解决未采取的分支无效的问题,
if constexpr
,否则这是我的建议。
foreach
函数并使用 SFINAE 确保选择正确的版本。
我会将您现在拥有的类型特征替换为检查提供的参数是否支持std::begin()
/
std::end()
的类型特征。我还会添加一种类型特征来检查提供的参数是否支持流式传输到 ostream
。
#include <type_traits>
#include <utility>
// void_t from c++20
// Early version to support old compilers that haven't implemented
// https://cplusplus.github.io/CWG/issues/1558.html
template<typename... Ts>
struct make_void { typedef void type; };
template<typename... Ts>
using void_t = typename make_void<Ts...>::type;
//----------------------------------------------------------------
// trait to check if T supports std::begin and std::end
template <class, class = void>
struct has_begin_and_end : std::false_type {};
template <class T>
struct has_begin_and_end<T, void_t<decltype(std::begin(std::declval<T&>()),
std::end(std::declval<T&>()))>>
: std::true_type {};
// trait to check if T supports streaming to an ostream
template <class, class = void>
struct can_ostream : std::false_type {};
template <class T>
struct can_ostream<
T, void_t<decltype(std::declval<std::ostream&>() << std::declval<T>())>>
: std::true_type {};
您的 foreach
版本可能如下所示:
// char[N] version - trims off `\0` if there is one
template <std::size_t N>
std::string foreach (const char (&arr)[N]) {
return {arr, arr + N - (arr[N - 1] == '\0')};
}
// std::string version, adds quotes around the string
std::string foreach (const std::string& str) {
return '"' + str + '"';
}
// forward declaration of the std::pair version
template <class F, class S>
std::string foreach (const std::pair<F, S>& pair);
// generic other non-container versions that requires that T can be streamed
// and that T does _not_ support std::begin/std::end
template <class T>
typename std::enable_if<can_ostream<T>::value && !has_begin_and_end<T>::value,
std::string>::type
foreach (const T& var) {
std::ostringstream listn;
listn << var;
return listn.str();
}
template <class T> // container version:
typename std::enable_if<has_begin_and_end<T>::value,
std::string>::type
foreach (const T& container) {
std::ostringstream listn;
listn << '{';
auto it = std::begin(container);
auto end = std::end(container);
if (it != end) {
listn << foreach (*it);
for (++it; it != end; ++it) {
listn << ", " << foreach (*it);
}
}
listn << '}';
return listn.str();
}
template <class F, class S> // pair version
std::string foreach (const std::pair<F, S>& pair) {
return '{' + foreach (pair.first) + '=' + foreach (pair.second) + '}';
}
您可以在这个
演示中看到它的工作原理。