Злоупотребление оператором запятой

Я ищу простой способ создать массив строк во время компиляции. Для теста я собрал класс с именем, 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"; 

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

12.10.2009 22:16:34
Ух ты, я слышал много шуток по поводу перегрузки оператора запятой. Я никогда не ожидал, что кто-то действительно сделает это.
Carl Norum 12.10.2009 22:42:16
@Carl: А как насчет boost.org/doc/libs/1_40_0/libs/assign/doc/index.html#intro (очень похоже на то, что хочет OP)
UncleBens 12.10.2009 22:46:31
вздрогнуть, положив гвозди в свой гроб. Как вы собираетесь отлаживать это, когда он начинает работать неправильно?
Carl Norum 12.10.2009 22:55:44
@Carl: Вы имеете в виду прямо сейчас? :)
Ed S. 12.10.2009 23:27:41
Для скриптового интерфейса я бы настоятельно рекомендовал не использовать C ++. Вам, вероятно, лучше выбрать что-то вроде Lua ( lua.org ), которое было разработано так, чтобы быть написанным на скрипте и встроенным. Если мы попытаемся сделать C ++ более дружелюбным, то попадем в глубокие и темные места.
Greg Hewgill 13.10.2009 01:45:42
7 ОТВЕТОВ
РЕШЕНИЕ

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

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

int a=3, b=4

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

Strings s="Hello", stringliteral

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

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

14
12.10.2009 22:35:36
Риск быть утомительно педантичным, Strings s="Hello";эквивалентно Strings s = Strings("Hello");. Это требует, чтобы копия ctor была вызываемой и даже могла вызывать ее для создания sиз временного объекта. Но оптимизация, чтобы заменить его прямой инициализацией с использованием const char*конструктора для s, без каких-либо временных, разрешена и распространена.
Steve Jessop 12.10.2009 22:45:59
Не могу поверить, что я этого не видел.
miked 13.10.2009 00:42:09

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

5
12.10.2009 22:52:39

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

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

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

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

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

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

Скорее всего, это сработает, но сокращенный синтаксис, вероятно, не стоит хитрой семантики, которая идет с ним.

8
12.10.2009 22:34:56
Можно ли заставить это работать? Проблема , которую я предвижу , что вы не можете перегрузить operator,для const char*, так ("Hello","world")это точно так же , как "world".
Steve Jessop 12.10.2009 22:41:21
Я предположил, что вы можете перегрузить оператор (const char *, const char *). Есть ли причина, почему это нельзя сделать? Честно говоря, я мало занимаюсь программированием на С ++.
Igor ostrovsky 13.10.2009 22:10:52

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

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

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

Strings s(input);

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

0
12.10.2009 22:37:23
Или шаблон конструктор: template <int N> Strings(const char* (&input)[N]) { for (int i = 0; i < N; ++i) push_back(input[i]); }. Тогда никакой нулевой терминатор не требуется: вы можете сделать const char *input[] = { "this", "that"}; Strings s(input);. Не уверен, что это действительно того стоит, но весело, если вам нравятся такие вещи.
Steve Jessop 12.10.2009 23:00:58
Да, или, конечно, шаблон конструктора может вызывать другую функцию, аналогичную vector::insert(iterator, InputIterator, InputIterator), чтобы избежать создания дублирующегося кода, если вы вызываете его с множеством разных длин в разных местах.
Steve Jessop 12.10.2009 23:06:53

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

std::vector<std::string> v = { "xyzzy", "plugh", "abracadabra" };
std::vector<std::string> v{ "xyzzy", "plugh", "abracadabra" };
0
12.10.2009 23:02:44

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

using namespace boost::assign;
vector<string> listOfThings;
listOfThings += "Hello", "World!";
0
12.10.2009 22:46:35

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

#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;
}
1
12.10.2009 23:24:16