Злоупотребление оператором запятой - PullRequest
9 голосов
/ 13 октября 2009

Я ищу простой способ создать массив строк во время компиляции. Для теста я собрал класс с именем Strings, который имеет следующие члены:

Strings(); 
Strings(const Strings& that);
Strings(const char* s1);
Strings& operator=(const char* s1);
Strings& operator,(const char* s2);

Используя это, я могу успешно скомпилировать код следующим образом:

Strings s;
s="Hello","World!";

Часть s="Hello" вызывает operator=, который возвращает Strings&, а затем operator, вызывается для "World!".

То, что я не могу заставить работать (в MSVC, еще не пробовал другие компиляторы), это

Strings s="Hello","World!";

Я бы предположил, что Strings s="Hello" вызовет конструктор копирования, и тогда все будет вести себя так же, как в первом примере. Но я получаю ошибку: error C2059: syntax error : 'string'

Однако, это прекрасно работает:

Strings s="Hello"; 

Итак, я знаю, что конструктор копирования работает хотя бы для одной строки. Есть идеи? Мне бы очень хотелось, чтобы второй метод работал, чтобы сделать код немного чище.

Ответы [ 7 ]

14 голосов
/ 13 октября 2009

Я думаю, что запятая в вашем втором примере - это не оператор запятой, а скорее элемент грамматики для объявлений нескольких переменных.

например, так же, как вы можете написать:

int a=3, b=4

Мне кажется, что вы, по сути, пишете:

Strings s="Hello", stringliteral

Таким образом, компилятор ожидает, что элемент после запятой будет именем переменной, и вместо этого он видит строковый литерал и объявляет ошибку. Другими словами, конструктор применяется к «Hello», но впоследствии запятая является , а не оператором запятой Strings.

Кстати, конструктор на самом деле не является конструктором копирования. Он создает объект Strings из буквального строкового параметра ... Термин конструктор копирования обычно применяется к тому же типу.

8 голосов
/ 13 октября 2009

Я бы не рекомендовал этот вид API. Вы продолжите обнаруживать случаи, которые не работают должным образом, поскольку запятая является оператором с наименьшим приоритетом. Например, этот случай также не будет работать:

if ("Hello","world" == otherStrings) { ... }

Возможно, вы сможете заставить все работать, если будете использовать скобки каждый раз вокруг набора строк, например:

Strings s=("Hello","World!");

И мой пример выше будет выглядеть так:

if (("Hello","world") == otherStrings) { ... }

Это, вероятно, можно заставить работать, но сокращенный синтаксис, вероятно, не стоит хитрой семантики, которая идет с ним.

5 голосов
/ 13 октября 2009

Использование boost::list_of.

1 голос
/ 13 октября 2009

Возможно сделать эту работу, для достаточно свободного определения «работа». Вот рабочий пример, который я написал в ответ на аналогичный вопрос несколько лет назад. Это был забавный вызов, но я бы не стал использовать его в реальном коде:

#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>

void f0(std::vector<int> const &v) { 
    std::copy(v.begin(), v.end(), 
        std::ostream_iterator<int>(std::cout, "\t"));
    std::cout << "\n";
}

template<class T>
class make_vector {
    std::vector<T> data;
public:
    make_vector(T const &val) { 
        data.push_back(val);
    }

    make_vector<T> &operator,(T const &t) {
        data.push_back(t);
        return *this;
    }

    operator std::vector<T>() { return data; }
};

template<class T> 
make_vector<T> makeVect(T const &t) { 
    return make_vector<T>(t);
}

int main() { 
    f0((makeVect(1), 2, 3, 4, 5));
    f0((makeVect(1), 2, 3));
    return 0;
}
0 голосов
/ 13 октября 2009

Если единственное задание Strings - хранить список строк, то boost::assign может лучше справиться с работой со стандартными контейнерами, я думаю:)

using namespace boost::assign;
vector<string> listOfThings;
listOfThings += "Hello", "World!";
0 голосов
/ 13 октября 2009

Если вы c ++ 0x, у них есть новые списки инициализаторов для этого! Я хотел бы, чтобы вы могли использовать их. Например:

std::vector<std::string> v = { "xyzzy", "plugh", "abracadabra" };
std::vector<std::string> v{ "xyzzy", "plugh", "abracadabra" };
0 голосов
/ 13 октября 2009

Вы можете использовать массив символьных указателей

Strings::Strings(const char* input[]);

const char* input[] = {
  "string one",
  "string two",
  0};

Strings s(input);

и внутри конструктора итерируйте указатели, пока не достигнете нуля.

...