void main(){
int[3] arr = [1, 2, 3,];
}
Is the extra comma legal or is it not flagged as error because of a compiler bug? I have many mixins that generate arrays with the extra comma at the end. I would like to know if I should taken the time to remove them.
even this compiles without errors:
void main(){
int[3] arr = [1, 2, 3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,];
}
I believe it's legal in order to allow for templates (or even mixins) to work in a generic manner:
template Foo(T) { } //What if Foo is empty like this?
auto arr = [1, 2, Foo!(int), Foo!(long)];
// [1, 2, , ]
It makes templates much easier to work with, so that you don't have to special-case against special outputs.
A more realistic example:
template Iota(size_t start, size_t end) //All integers in range [start, end)
{
static if (start < end)
alias TypeTuple!(start, Iota!(start + 1, end)) Iota;
else
alias TypeTuple!() Iota;
}
auto arr1 = [-10, Iota!(0, 3)]; // arr is now [-10, 0, 1, 2]
auto arr2 = [-10, Iota!(a, b)]; // arr is now [-10, a .. b]
Now what happens if a is equal to b? Then arr2 decays to [-10, ].
It's allowed in many languages to allow code formatting like:
string[3] arr = [
"Some long String",
"And another",
"etc, etc, etc",
];
without having to omit the comma from the last value.
Java permits such an array initializer too.
I'm 99% sure the single comma is by design. The 2nd, 3rd, etc.? IMHO, it's a bug in the design or implementation, but I don't know which.
Some months ago Walter committed this behavior into dmd. Before, a trailing comma was sometimes allowed and sometimes not, and if you're in dmd1 land, you're stuck with that.
Now, for dmd2, at least, a trailing comma should always be valid in an array literal, as well as in parameter lists, argument lists, and template argument lists.
The multiple trailing commas, however, is a bug in the implementation.
Related
So I am playing around with some arrays, and I cannot figure out why this won't work.
int numbers[5] = {1, 2, 3};
int values[5] = {0, 0, 0, 0, 0};
values = numbers;
The following error appear:
Error 1 error C2106: '=' : left operand must be l-value c:\users\abc\documents\visual studio 2012\projects\consoleapplication7\consoleapplication7\main.cpp 9 1 ConsoleApplication7
Why can't I do like that? What does the error mean?
Arrays have a variety of ugly behavior owing to C++'s backward compatibility with C. One of those behaviors is that arrays are not assignable. Use std::array or std::vector instead.
#include <array>
...
std::array<int,5> numbers = {1,2,3};
std::array<int,5> values = {};
values = numbers;
If, for some reason, you must use arrays, then you will have to copy the elements via a loop, or a function which uses a loop, such as std::copy
#include <algorithm>
...
int numbers[5] = {1, 2, 3};
int values[5] = {};
std::copy(numbers, numbers + 5, values);
As a side note, you may have noticed a difference in the way I initialized the values array, simply providing an empty initializer list. I am relying on a rule from the standard that says that if you provide an initializer list for an aggregate, no matter how partial, all unspecified elements are value initialized. For integer types, value initialization means initialization to zero. So these two are exactly equivalent:
int values[5] = {0, 0, 0, 0, 0};
int values[5] = {};
You can't assign arrays in C++, it's stupid but it's true. You have to copy the array elements one by one. Or you could use a built in function like memcpy or std::copy.
Or you could give up on arrays, and use std::vector instead. They can be assigned.
Array names are constant not modifiable l-value, you can't modify it.
values = numbers;
// ^
// is array name
Read compiler error message: "error C2106: '=' : left operand must be l-value" an l-value is modifiable can be appear at lhs of =.
You can assign array name to a pointer, like:
int* ptr = numbers;
Note: array name is constant but you can modify its content e.g. value[i] = number[i] is an valid expression for 0 <= i < 5.
Why can't I do like that?
Basically this constrain is imposed by language, internally array name uses as base address and by indexing with base address you can access content continue memory allocated for array. So in C/C++ array names with be appear at lhs not a l-value.
values = numbers;
Because numbers and values are pointers.
int numbers[5] = {1, 2, 3};
int values[5] = {0, 0, 0, 0, 0};
for(int i = 0;i < 5; i ++){
values[i] = numbers[i];
}
std::array is a good idea, but this is also possible:
struct arr { int values[5]; };
struct arr a{{1, 2, 3}};
struct arr b{{}};
a = b;
Otherwise, use std::memcpy or std::copy.
I think the reason for non-assignability is that C wants to make sure that any pointers to any element in the array would pretty much always remain valid.
Think about vectors that allow dynamic resizing: the older pointers become invalid (horribly) after resizing happens automatically.
PS. With the design philosophy for vectors, the following block works well.
vector<int> v = {1, 5, 16, 8};
vector<int> v_2 = {7, 5, 16, 8};
v_2 = v;
/info is a string/
while(getline(cin,info)){
char *a = new char[info.size()+1];
a[info.size()] = 0;
memcpy(a,info.c_str(),info.size());
//cout<<sizeof(a)<<endl;
set<char *>m;
m.insert(a,a+8);
}
//Error:invalid conversion from char to char*
I suspect the thing you're missing is that when you insert a range, it inserts each item in that range. It doesn't try to insert them as a single item but it inserts each item in the range individually.
set<char *>m;
m.insert(a,a+8);
Okay, so m is a set of char*. You've said that you want to insert everything in the range from a to a+8. That's a range of characters. So you're trying to insert characters into a set of pointers to characters. That can't work.
Perhaps you just want m.insert(a);? Since m is a set of char* and a is a char*, I can't imagine what you might want to do other than insert it.
m.insert(a,a+8);
You tried to use a wrong overload of set::insert(). It is mostly useful when you want to do something like this:
int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
std::set<int> m;
m.insert(a[0]);
m.insert(a[1]);
...
m.insert(a[6]);
m.insert(a[7]);
This is lengthy and we want to avoid repetitions. Equivalently, you can write
int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
std::set<int> m;
m.insert(a, a+8);
Now let's go back to your code...
while(getline(cin,info)){
char *a = new char[info.size()+1];
...
set<char *>m;
m.insert(a,a+8);
Conversely, you can "expand" it into
while(getline(cin,info)){
char *a = new char[info.size()+1];
...
set<char *>m;
m.insert(a[0]);
m.insert(a[1]);
...
m.insert(a[6]);
m.insert(a[7]);
Now you see you ended up in insert-ing a char object to std::set<char *>. This is the reason why you got the error message.
Proposed fix
You defined set<char *> m inside the while loop. That means you are dealing with a freshly constructed m in each iteration of the while loop, which doesn't makes sense. You need to move the definition of m to before while.
It is not recommended to use new char[...] until you spend 5--6 years in C++. C++ already prepared std::string and/or std::vector for you to replace 99% of the use cases of raw new char[...]. So your set should be set<string> m.
std::string has an assignment operator (and a copy constructor) which takes a std::string or NUL-terminated const char * and does memcpy() for you, and you can think set::insert() internally invokes it.
std::string also supports a substring operation returning a fresh std::string such that string("abcde").substr(3) == "abc".
http://en.cppreference.com/w/cpp/string/basic_string/substr
That means you don't need an intermediate variable a at all.
After these corrections, you'll get
set<string> m;
while(getline(cin,info)){
m.insert(info.substr(8));
}
I want to be able to write in C++ something similar to the following Python code:
if x in [1, 2, 3, 5] ...
to test whether an element is contained in a set of hard-coded values, defined in-place. Like this:
if (in(x, {1, 2, 3, 5})) ...
Here is the possible implementation of the in function:
template<class T>
bool in(const T& x, std::initializer_list<T> c)
{
return std::find(c.begin(), c.end(), x) != c.end();
}
My question is: do I really have to write this function by myself? Are there any default implementations over there? Maybe in boost? I checked boost::contains, but it works only with strings.
If you have access to c++20 you can use set's contains which returns a bool allowing you to do:
if(set{ 4, 8, 15, 16, 23, 42 }.contains(x))
Live Example
Otherwise, with just c++11 you can still use set's count which only returns 1 or 0 allowing you to do something like:
if(set<int>{ 4, 8, 15, 16, 23, 42 }.count(x) > 0U)
Live Example
Keep in mind that magic numbers can be confusing for your audience (and cause 5 seasons of Lost.)
I'd recommend declaring your numbers as a const initializer_list<int> and giving them a meaningful name:
const auto finalCandidates{ 4, 8, 15, 16, 23, 42 };
if(cend(finalCandidates) != find(cbegin(finalCandidates), cend(finalCandidates), x))
boost::algorithm::contains doesn't only work on strings, it works on any range, i.e. a sequence that can yield a begin and end iterator. To find a single value use it as follows:
auto l = {1,2,3,4};
auto l1 = {2}; // thing you want to find
if(boost::algorithm::contains(l, l1)) { ... }
You can perform your search using the standard library only, but doing so is quite a bit more verbose. A couple of options are:
using a lambda
if(std::any_of(l.begin(), l.end(),
[](int i){ return i == 2; })) { ... }
using std::bind
using std::placeholders::_1;
if(std::any_of(l.begin(), l.end(),
std::bind(std::equal_to<>(), 2, _1)) { ... }
Live demo
Note that std::equal_to<>() is a C++14-only option. For a C++11 compiler, use std::equal_to<int>().
Indeed the STL does not have a simple std::contains() function. Recently, there was a discussion on reddit about this topic.
Unfortunately, what came out of this is that it is considered harmful to have std::contains(), since it encourages people to write slow algorithms. Think for instance of
if (!std::contains(my_set.begin(), my_set.end(), entry)) {
my_set.insert(entry);
}
This code example essentially searches for the correct position twice: Once in contains, and once to find the insert location.
In my opinion, it would still be very helpful to have std::contains(), but so far no one was convinced yet to write a proposal.
So either use boost (as suggested by other in this thread), or write your own function which you essentially already did :-)
I posted the following code on rosettacode.org for the task of converting Arabic and Roman numerals.
import std.regex, std.array, std.algorithm;
immutable {
int[] weights = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
string[] symbols = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX",
"V", "IV", "I"];
}
string toRoman(int n) {
auto app = appender!string;
foreach (i, w; weights) {
while (n >= w) {
app.put(symbols[i]);
n -= w;
}
if (n == 0) break;
}
return app.data;
}
int toArabic(string s) {
int arabic;
foreach (m; match(s, "CM|CD|XC|XL|IX|IV|[MDCLXVI]")) {
arabic += weights[symbols.indexOf(m.hit)];
}
return arabic;
}
It used to work just fine, but now I get a compiler error.
Error: template
std.algorithm.indexOf(alias pred = "a
== b",R1,R2) if (is(typeof(startsWith!(pred)(haystack,needl
e)))) does not match any function
template declaration
According to the documentation indexOf is deprecated, and countUntil should be used in stead, but it gives me the same error.
Long story but I'll try to keep it short:
std.algorithm.indexOf expects an input range, which is a structural type that must define front, popFront() and empty. For arrays, these methods are defined in std.array and work via uniform function call syntax, which allows fun(someArray) to work the same as someArray.fun().
immutable string[] is not an input range, since popFront removes the first element of the array, which cannot be done for an immutable type. The fact that this used to work was a bug.
I've updated the Rosetta Code entry to change symbols to an immutable(string)[]. Here, the elements of symbols are immutable, but the array may be sliced and reassigned. For example:
void main() {
immutable string[] s1 = ["a", "b", "c"];
immutable(string)[] s2 = ["d", "e", "f"];
s2 = s2[1..$]; // This is what std.array.popFront does under the hood.
assert(s2 == ["e", "f"]); // Passes.
s2[1] = "g"; // Error: Can't modify immutable data.
s1 = s1[1..$]; // Error: Can't modify immutable data.
s1[1] = "g"; // Error: Can't modify immutable data.
}
immutable string[] is implicitly convertible to immutable(string)[] but implicit function template instantiation (often denoted IFTI; this is what's used to instantiate the indexOf template) is not smart enough try this.
I believe this is a bug in std.algorithm. If you remove the immutable qualifier, the code works as is. I think indexOf/countUntil should work on immutable arrays, but at the moment it does not.
You can make them manifest constants (precede each declaration with enum) and it appears to work. Amusingly, this may also be a bug.
Apologies for the breakage; I introduced it. I concur with dsimcha's description and proposed fix.
We are considering a simple change to the language to account for this simple case. That would automatically peel off one level of qualifiers when passing a value of a qualified type into a function. By that (for now hypothetical) rule, qualifier(T[]) would become (when passed to a function) qualifier(T)[] and qualifier(T*) would become qualifier(T)*. This would allow your example to work. The disadvantage is that a function would not be able to distinguish the top-level qualifier but I believe that that does not harm any concrete use.
I use the following template to obtain a pointer pointing after the last element of an array:
template <typename T, size_t n>
T* end_of(T (&array)[n])
{
return array + n;
}
Now I seem to remember that there was some problem with this approach, but I cannot remember what it was. I believe it had something to with the choice of the type parameters or function parameters, but I'm not sure. So just as a sanity check, do you see any problems with the above code? Small usage test:
int test[] = {11, 19, 5, 17, 7, 3, 13, 2};
std::sort(test, end_of(test));
Your proposal is not necessarily evaluated at compile time, it depends on optimisation. The following is calculated at compile time:
template <typename T, size_t N> char (&array(T(&)[N]))[N];
int main()
{
int myArray[10];
std::cout << sizeof array(myArray) << std::endl;
return 0;
}
It works by creating an array type of char which is the same number of elements as the given array. sizeof always returns size in number of chars.
The only problem i see is that if you ever don't know the length at compile time, your template won't know what to put in there. So you'd have to say test+x or something anyway, and now you have two different ways to do the same thing.
Personally i'd rather just use a vector<int> and thus have end() already defined for me. If you ever need the array, it's available as &v[0].
You need a const version too. However, as far as I know, there's no actual problems with that approach- I see it used commonly.