2017-05-11 37 views
2

我在我的代码重载函数与类型签名:消除歧义列表初始化的std ::矢量<std::string>

void foo(std::string); 
void foo(std::vector<std::string>); 

我想foo​​的用户能够与一个字符串调用或串

//Use case 1 
foo("str"); 

//Use case 2 
foo({"str1","str2","str3"}); 
foo({"str1","str2","str3","str4"}); 

的问题列表是当调用者传递串到foo的初始化列表。

//Problem! 
foo({"str1","str2"}); 

这个对foo的调用是不明确的,因为它匹配两个类型签名。 这是因为显然{"str1","str2"}std::string

所以我的问题是有效的构造函数有什么我可以在声明中或实施富这样的,我保持我上述没有碰到这种模棱两可的构造情况下,API的事情。

我不想定义我自己的字符串类,但是我可以定义别的东西而不是vector<string>,只要它可以用字符串的初始化列表初始化即可。

只是出于好奇,为什么字符串构造函数接受{"str1","str2"}

回答

3

{"str1","str2"}std::string构造函数接受两个迭代器相匹配。构造函数6 here。它会尝试从“str1”的开始迭代到未定义行为的“str2”开始之前。

您可以通过为std::initializer_list<const char*>引入过载来解决这个歧义问题,该过载转发给std::vector<std::string>过载。

void foo(std::string); 
void foo(std::vector<std::string>); 

void foo(std::initializer_list<const char*> p_list) 
{ 
    foo(std::vector<std::string>(p_list.begin(), p_list.end())); 
} 
+0

伟大的这正是我一直在寻找!谢谢。 – Ross

2

您可以通过使用可变参数模板来稍微更改您的API,以避免您遇到的歧义。

template <typename... Ts> 
auto foo(Ts...) 
    -> std::enable_if_t<all_are_convertible_to<std::string, Ts...>, void> 
{ 
    /* ... */ 
} 

用法:

foo("aaaa"); 
foo("aaaa", "bbb", "cc", "d"); 

在C++ 17,all_are_convertible_to可以与倍表达(或std::conjunction)来实现:

template <typename T, typename... Ts> 
inline constexpr bool are_all_convertible = 
    (std::is_convertible_v<Ts, T> && ...); 

在C++ 11可以实现某种类型的递归的特征如下:

template <typename, typename...> 
struct are_all_convertible_to_helper; 

template <typename T, typename X, typename... Xs> 
struct are_all_convertible_to_helper<T, X, Xs...> 
    : std::integral_constant<bool, 
     std::is_convertible<X, T>::value && are_all_convertible_to_helper<T, Xs...>::value 
    > 
{ 
}; 

template <typename T> 
struct are_all_convertible_to_helper<T> : std::true_type 
{ 
};