Abusing the comma operator
I'm looking for an easy way to build an array of strings at compile time. For a test, I put together a class named Strings
that has the following members:
Strings();
Strings(const Strings& that);
Strings(const char* s1);
Strings& operator=(const char* s1);
Strings& operator,(const char* s2);
Using this, I can success开发者_Python百科fully compile code like this:
Strings s;
s="Hello","World!";
The s="Hello"
part invokes the operator=
which returns a Strings&
and then the operator,
get called for "World!"
.
What I can't get to work (in MSVC, haven't tried any other compilers yet) is
Strings s="Hello","World!";
I'd assume here that Strings s="Hello"
would call the copy constructor and then everything would behave the same as the first example. But I get the error: error C2059: syntax error : 'string'
However, this works fine:
Strings s="Hello";
So I know that the copy constructor does at least work for one string. Any ideas? I'd really like to have the second method work just to make the code a little cleaner.
I think that the comma in your second example is not the comma operator but rather the grammar element for multiple variable declarations.
e.g., the same way that you can write:
int a=3, b=4
It seems to me that you are essentially writing:
Strings s="Hello", stringliteral
So the compiler expects the item after the comma to be the name of a variable, and instead it sees a string literal and announces an error. In other words, the constructor is applied to "Hello", but the comma afterwards is not the comma operator of Strings.
By the way, the constructor is not really a copy constructor. It creates a Strings object from a literal string parameter... The term copy constructor is typically applied to the same type.
I wouldn't recommend this kind of an API. You are going to continue discovering cases that don't work as expected, since comma is the operator with the lowest precedence. For example, this case won't work either:
if ("Hello","world" == otherStrings) { ... }
You may be able to get things working if you use brackets every time around the set of strings, like this:
Strings s=("Hello","World!");
And my example above would look like this:
if (("Hello","world") == otherStrings) { ... }
That can likely be made to work, but the shorthand syntax is probably not worth the tricky semantics that come with it.
Use boost::list_of
.
It's possible to make this work, for a sufficiently loose definition of "work." Here's a working example I wrote in response to a similar question some years ago. It was fun as a challenge, but I wouldn't use it in real code:
#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;
}
You could use an array of character pointers
Strings::Strings(const char* input[]);
const char* input[] = {
"string one",
"string two",
0};
Strings s(input);
and inside the constructor, iterate through the pointers until you hit the null.
If the only job of Strings
is to store a list of strings, then boost::assign
could do the job better with standard containers, I think :)
using namespace boost::assign;
vector<string> listOfThings;
listOfThings += "Hello", "World!";
If you c++0x, they have new inializer lists for this! I wish you could use those. For example:
std::vector<std::string> v = { "xyzzy", "plugh", "abracadabra" };
std::vector<std::string> v{ "xyzzy", "plugh", "abracadabra" };
精彩评论