16

Under what circumstances do extra grouping parentheses break things in C++ (C++11 specifically)? For reasons that are not relevant here, I ended up at one point with an expression that had an extra, unnecessary set of parens around it, and I discovered that the C++11 typeinfo function is_same was determining it to be a different type than the same code without the parentheses. Here is a boiled-down example of the somewhat baffling behaviour:

#include <iostream>
using namespace std;

int main()
{
  string s = "foo";
  cout << std::is_same<decltype(s), decltype(string("foo"))>::value;
  cout << std::is_same<decltype(s), decltype((s))>::value;
  cout << std::is_same<decltype((s)), decltype(string("foo"))>::value;
  cout << std::is_same<decltype((s)+"x"), decltype(string("foo")+"x")>::value;

  return 0;
}

This code prints "1001", which seems to indicate that the extra parens in the middle two lines cause the expression to be of a different type, but using that parenthesised expression in a larger expression makes it once again the same type. On the other hand, if I use typeid to get a name for the type, typeid(s) and typeid((s)) seem to produce the same thing.

I've now worked around the immediate problem, but I still don't understand why this happens in the first place; searching around for "double parentheses c++" and the like doesn't seem to turn up anything relevant (mostly pages about operator overloading, and compiler extensions that only activate after a specific keyword).

So: what the heck is going on here? Why is the type of s different from the type of (s)?

blahedo
  • 834
  • 8
  • 19
  • 9
    There's a special rule for `decltype` that applies if the expression inside the `decltype`-parens is an *id-expression* or a *class-member-access*. Adding a second pair of parens makes it a *primary-expression* (but not itself an *id-expression* or *class-member-access*), so the special rule doesn't apply. See, e.g., http://stackoverflow.com/q/14115744/420683 or http://stackoverflow.com/q/3097779/420683 – dyp Feb 02 '14 at 21:43
  • 2
    Also, `foo((1, 2));` has a different meaning than `foo(1, 2);`. Is this question generally about parentheses or about `decltype`? – dyp Feb 02 '14 at 21:51
  • 1
    Parentheses can also make a difference when applying a unary `&`: `struct foo { int m; void bar() { auto x = &foo::bar; auto y = &(foo::bar); } };` Here, `x` is of the type `int (foo::*)`, whereas `y` is of the type `int*`. But that's not specific to C++11. – dyp Feb 02 '14 at 22:15
  • 1
    @dyp About that last one: I think you mean `foo::m` rather than `foo::bar`. –  Feb 02 '14 at 22:20
  • 1
    @hvd Oops, of course. Too late for fixing it :( [Live example](http://coliru.stacked-crooked.com/a/a933035b24dc85dd) (with fix). – dyp Feb 02 '14 at 22:21
  • It seems @dyp's reference to http://stackoverflow.com/q/3097779/420683 has the answer. See e.g. https://ideone.com/FqrcEy, so that `decltype(s)` gives `string` and `decltype((s))` becomes `string &`. – NonNumeric Feb 02 '14 at 22:24
  • @dyp could you explain your `foo(1,2)` example? Or did you mean the ADL-disabling `(foo)(1,2)`? – TemplateRex Feb 03 '14 at 11:25
  • @TemplateRex `foo((1,2))` is a function call with only *one* argument expression, namely `(1,2)` is a single argument of value `2`. And disabling ADL is another nice example where parens matter :) – dyp Feb 03 '14 at 11:28

1 Answers1

2

decltype treats its arguments differently depending on the additional parentheses.

The second pair of parentheses makes it a primary-expression (but not itself an id-expression or class-member-access), so the special rule doesn't apply.

Here is a discussion: Significance of parentheses in decltype((c))?

Community
  • 1
  • 1
Sergey K.
  • 24,894
  • 13
  • 106
  • 174