Как предотвратить использование std::string конструктором initializer_list?
Я хочу, чтобы следующий код выводил "test" вместо "X" для случая при использовании std::string
используя ту же инициализацию, что и другие основные типы. std::string
теперь вызывает конструктор с initializer_list
и, следовательно, специализация шаблона get
за char
называется.
#include <sstream>
#include <string>
#include <iostream>
// Imagine this part as some kind of cool parser.
// I've thrown out everything for a simpler demonstration.
template<typename T> T get() {}
template<> int get(){ return 5; }
template<> double get(){ return .5; }
template<> char get(){ return 'X'; }
template<> std::string get(){ return "test"; }
struct Config {
struct proxy {
// use cool parser to actually read values
template<typename T> operator T(){ return get<T>(); }
};
proxy operator[](const std::string &what){ return proxy{}; }
};
int main()
{
auto conf = Config{};
auto nbr = int{ conf["int"] };
auto dbl = double{ conf["dbl"] };
auto str = std::string{ conf["str"] };
std::cout << nbr << std::endl; // 5
std::cout << dbl << std::endl; // 0.5
std::cout << str << std::endl; // 'X'
}
Есть ли хороший способ сделать это, не нарушая согласованный вид инициализации переменных?
2 ответа
std::string
имеет конструктор, который принимает initializer_list<char>
аргумент; этот конструктор всегда будет учитываться первым, когда вы используете list-initialization с непустым braced-init-list, поэтому char
специализация get()
подбирается
Если вы используете скобки вместо скобок для всех инициализаций, initializer_list
конструктор больше не будет единственным, рассмотренным в std::string
дело.
auto nbr = int( conf["int"] );
auto dbl = double( conf["dbl"] );
auto str = std::string( conf["str"] );
Однако само по себе это изменение не работает, потому что у вас есть неявный пользовательский шаблон преобразования, который может давать любой тип. Код выше, в std::string
случай, результаты в матчах для всех std::string
конструкторы, которые могут быть вызваны с одним аргументом. Чтобы это исправить сделайте оператор преобразования explicit
,
struct proxy {
// use cool parser to actually read values
template<typename T>
explicit operator T(){ return get<T>(); }
};
Теперь только явное преобразование в std::string
является жизнеспособным, и код работает так, как вы хотите.
auto nbr = (int)conf["int"];
auto dbl = (double)conf["dbl"];
auto str = (string&&)conf["str"];
Вы определили шаблонный оператор T(), вышеприведенный просто вызывает его. сделать копию, вы можете
auto str = string((string&&)conf["str"])
РЕДАКТИРОВАТЬ: изменено (строка) на (строка &&)
EDIT2: следующие работы также (проверили их все - gcc -std= C++11):
auto nbr = (int&&)conf["int"];
auto dbl = (double&&)conf["dbl"];
auto str = (string&&)conf["str"];