这是可能的,但你要去的方向不是一个明智的方向。
相反,我建议通过鸭式打字来思考事情。如果你的函数需要遍历传入的东西的内容,并且期望内容是字符串,那么你正在寻找的是“这个参数与std::string
s 序列的概念相匹配”。
这最好通过 requires 语法在 C++14 中完成,但您可能还没有符合 C++14 的编译器,除非您通过时间机器发布。
我们可以在 C++11 中通过特征类、SFINAE 和enable_if
. 或者,我们可以在 C++11 中通过特征类和标签调度来做到这一点。标签调度没有 SFINAE 那么神秘,所以我将在这里进行演示。
我们从一个通用函数开始:
template<typename C>
void function( C const& c )
接受任何东西。接下来,我们编写一些特征类:
template<typename C,typename=void>
struct is_string_sequence : std::false_type {};
template<typename C>
struct is_string_sequence<C, typename std::enable_if<TODO STUFF HERE>::type> : std::true_type {};
这告诉我们传入的参数是否是字符串上的序列。
接下来,我们编写一个辅助函数:
template<typename C>
void function_helper( C const& c, std::true_type string_sequence_test ) {
// your code goes here, and can assume c is a string sequence
}
并将第一个转发给它:
template<typename C>
void function( C const& c ) {
function_helper( c, is_string_sequence<C>() );
}
但我们也创建了一个标签来传递给助手。如果我们的测试通过,则此标签为 true,否则为 false。由于我们只有对 true 的覆盖,因此 false 的情况会产生错误,并且该错误往往是合理可读的。
剩下的就是写了TODO STUFF HERE
。
在 C++11 中判断某事物是否为字符串序列的鸭式方法是查看是否:
for( std::string s : c ) {
}
编译。这样做的一种方法是查看在和可见的上下文中应用的是否begin
和end
释放函数返回迭代器,并且这些迭代器在取消引用时产生可转换为的类型。c
std::begin
std::end
std::string
这需要一些体操。首先,查看在特定上下文中调用时的内容begin
和结果:end
namespace adl_helper {
using std::begin; using std::end;
template<typename C>
auto adl_begin( C&& c )->decltype( begin(std::forward<C>(c)) );
template<typename C>
auto adl_end( C&& c )->decltype( end(std::forward<C>(c)) );
}
using adl_helper::adl_begin; using adl_helper::adl_end;
请注意,这些没有主体:出于我们的目的,我们不需要主体,我们只需要返回类型。
现在我们可以问“某物是容器吗?”
template<typename C, typename=void>
struct is_container : std::false_type {};
template<typename C>
struct is_container<C, typename=typename std::enable_if<
std::is_convertible<
decltype( adl_begin( std::declval<C&>() ) == adl_end( std::declval<C&>() ) ),
bool
>::value
>::type> : std::true_type
{
typedef decltype( adl_begin( std::declval<C&>() ) ) iterator;
};
一个更高级的人可能会iterator_traits
改为调查(上面,我只是检查begin
andend
工作,结果==
在它们之间被覆盖,返回可以转换为的东西bool
)。
然后我们可以询问生成的迭代器是否具有可以std::string
在另一个特征类中转换的值类型。
或者,我们可以做 a decltype( *adl_begin( std::declval<C&>() ) )
,看看是否可以直接转换为std::string
,并假设是否begin
有效end
。
SFINAE 方法类似,除了将标签分派给助手之外,我们将测试直接放入function
签名中,包装在enable_if
激活或停用function
for 重载决议的 an 中。这会产生稍微难以阅读的错误,但根据我的经验,在调用堆栈中的位置更高。
你没有按照你的目标去做,因为容器如何从其他类型构建的细节并不重要:重要的是你如何使用它。
如果您需要除迭代之外的类型的其他功能,您可以编写其他鸭式测试来确定您是否可以擦除、插入等。但是,请注意关联容器和顺序容器是非常不同的东西,统一对待它们是很少是一个好主意,除了两者都是序列。