Topic: constants vs. ODR


Author: Johannes Schaub<schaub.johannes@googlemail.com>
Date: Thu, 28 Apr 2011 15:47:27 CST
Raw View
Balog Pal wrote:

>
>  If I have the following in a header:
>
>  const int NUM = 42;
>  inline void foo(const int&) { ... } // could be template, etc
>  inline void bar()
>  {
>   foo(NUM);
>  }
>
>  If I understand correctly, by the current standard this will break the one
>  definition rule, if the header is included in multiple translation units.
>  Because of the const hack that makes it static, so NUM will really be
>  different in every TU, therefore bar() will have non-identical
>  definitions. Placing the program in UB land.
>
>  My question is whether this was fixed for C++0x?
>

This wasn't really fixed, in my opinion. The ODR in the spec says that NUM
should be used in the way that "the value (but not the address) of the
object is used", in order to make the above example well-defined when
included into multiple TUs.

In your case, you bind the object to a reference, and I would say that this
crosses the boundary of allowed interpretation variety of that ODR rule.


--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Balog Pal"<pasa@lib.hu>
Date: Sun, 1 May 2011 10:14:40 CST
Raw View
[identical repost after 40 hours]
"Daniel Kr   gler"<daniel.kruegler@googlemail.com>
>  Am 28.04.2011 20:19, schrieb Balog Pal:
>>
>>   If I have the following in a header:
>>
>>   const int NUM = 42;
>>   inline void foo(const int&) { ... } // could be template, etc
>>   inline void bar()
>>   {
>>   foo(NUM);
>>   }
>>
>>   If I understand correctly, by the current standard this will break the
>>  one
>>   definition rule, if the header is included in multiple translation
>>  units.
>>   Because of the const hack that makes it static, so NUM will really be
>>   different in every TU, therefore bar() will have non-identical
>>  definitions.
>>   Placing the program in UB land.
>
>      in each definition of D, corresponding names, looked up according to
>  3.4, shall refer to an entity defined within the definition of D, or
>  shall refer to the same entity, after overload resolution (13.3) and
>  after matching of partial template specialization (14.8.3), except that
>  a name can refer to a const object with internal or no linkage if the
>  object has the same literal type in all definitions of D, and the object
>  is initialized with a constant expression (5.19), and the value (but not
>  the address) of the object is used, and the object has the same value in
>  all definitions of D;"

At the end it says "and the value (but not the address) of the object is
used".
What is teh meaning of "used" there? I interpreted it as "taken". The
function foo gets the argument by reference, that for me means taking (and
"using") its address.

If that is not really so, and taking the address this way is not use, it
should better be expressed in the standard.

>  You are not showing us any content of the body of foo(), so we don't
>  know whether foo() might violate the ODR when referring to it's function
>  argument. If foo would be written like this:

This remark quite implies that taking the address to do the function call is
not use, and the body is relevant.    IMO that is far from obvious if so.

If I look the code with compiler/code generator's eyes, foo will have code
identical as if it were foo(const int *). And call to foo will generate code
equivalent to passing&NUM. Resulting different bodies in all TUs.  If the
compiler does not do actual inlining, that is allowed, there's not much
choice of other ways.

>  #include<iostream>
>
>  const int NUM = 42;
>  inline void foo(const int&   arg) { std::cout<<   &arg<<   std::endl; }
>  inline void bar() { foo(NUM); }
>
>  it would violate the ODR, yes. The same could happen for an analogously
>  written function bar_2() as follows:
>
>  #include<iostream>
>
>  const int NUM_2 = 42;
>  inline void bar_2() { std::cout<<   &NUM_2<<   std::endl; }
>
>>   My question is whether this was fixed for C++0x?
>
>  What do you want to have fixed?

If my original interpretation is correct, then we don't need to use&
explicitly, still have the ODR on very common cases, especially for
templates, where taking arguments by const reference is the norm. That
should not be so.

If the provided interpretation is correct, and inside the function only the
value is really used without explicit taking of the address, then I'd like
to have a note or example with code like above stating it shal not worry on
the user side (and certainly be looked after by implementation).

The real world implementations I saw for inline use COMDEFs or __selectany
for the function body, meaning the code is generated into all objects. Then
the linker will use a random piece. By ODR they must be sufficiently
identical, if not it is UB anyway.

I'm thinking of an ill case from the above scenario. Let's change NUM's
initializer to a function call.

int ret_42(); // defined in a single TU as {return 42;}
const int NUM = ret_42();
inline void foo(const int&  arg) { std::cout<<   arg<<   std::endl;  }
inline void bar() { foo(NUM); }

Will bar print always 42?  What if it is called before main(), during
initialization of namespace variables?  If every function used its "own"
body, then NUM is initialized before that and have its value properly.

But if it used the selectany method, the executing body could use address of
NUM from another TU, one that will have that variable initialized only after
my call -- printing 0.

If using the value is what the standard really means, the implementation
will need some extra measures to have it.


--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Johannes Schaub <schaub.johannes@googlemail.com>
Date: Sun, 1 May 2011 12:52:58 CST
Raw View
Balog Pal wrote:

>
> [identical repost after 40 hours]
> "Daniel Kr   gler"<daniel.kruegler@googlemail.com>
>>  Am 28.04.2011 20:19, schrieb Balog Pal:
>>>
>>>   If I have the following in a header:
>>>
>>>   const int NUM = 42;
>>>   inline void foo(const int&) { ... } // could be template, etc
>>>   inline void bar()
>>>   {
>>>   foo(NUM);
>>>   }
>>>
>>>   If I understand correctly, by the current standard this will break the
>>>  one
>>>   definition rule, if the header is included in multiple translation
>>>  units.
>>>   Because of the const hack that makes it static, so NUM will really be
>>>   different in every TU, therefore bar() will have non-identical
>>>  definitions.
>>>   Placing the program in UB land.
>>
>>      in each definition of D, corresponding names, looked up according to
>>  3.4, shall refer to an entity defined within the definition of D, or
>>  shall refer to the same entity, after overload resolution (13.3) and
>>  after matching of partial template specialization (14.8.3), except that
>>  a name can refer to a const object with internal or no linkage if the
>>  object has the same literal type in all definitions of D, and the object
>>  is initialized with a constant expression (5.19), and the value (but not
>>  the address) of the object is used, and the object has the same value in
>>  all definitions of D;"
>
> At the end it says "and the value (but not the address) of the object is
> used".
> What is teh meaning of "used" there? I interpreted it as "taken". The
> function foo gets the argument by reference, that for me means taking (and
> "using") its address.
>
> If that is not really so, and taking the address this way is not use, it
> should better be expressed in the standard.
>

Please also see http://www.velocityreviews.com/forums/t740672-is-boost-_n-
placeholders-doing-undefined-behavior.html .

I agree that this is everything, except clear in the spec :)


--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: =?windows-1252?Q?Daniel_Kr=FCgler?= <daniel.kruegler@googlemail.com>
Date: Fri, 6 May 2011 13:07:13 CST
Raw View
Am 01.05.2011 18:14, schrieb Balog Pal:
>
> [identical repost after 40 hours]
> "Daniel Kr   gler"<daniel.kruegler@googlemail.com>
>> Am 28.04.2011 20:19, schrieb Balog Pal:
>>>
>>> If I have the following in a header:
>>>
>>> const int NUM = 42;
>>> inline void foo(const int&) { ... } // could be template, etc
>>> inline void bar()
>>> {
>>> foo(NUM);
>>> }
>>>
>>> If I understand correctly, by the current standard this will break the
>>> one
>>> definition rule, if the header is included in multiple translation
>>> units.
>>> Because of the const hack that makes it static, so NUM will really be
>>> different in every TU, therefore bar() will have non-identical
>>> definitions.
>>> Placing the program in UB land.
>>
>>     in each definition of D, corresponding names, looked up according to
>> 3.4, shall refer to an entity defined within the definition of D, or
>> shall refer to the same entity, after overload resolution (13.3) and
>> after matching of partial template specialization (14.8.3), except that
>> a name can refer to a const object with internal or no linkage if the
>> object has the same literal type in all definitions of D, and the object
>> is initialized with a constant expression (5.19), and the value (but not
>> the address) of the object is used, and the object has the same value in
>> all definitions of D;"
>
> At the end it says "and the value (but not the address) of the object is
> used".
> What is teh meaning of "used" there? I interpreted it as "taken". The
> function foo gets the argument by reference, that for me means taking (and
> "using") its address.
>
> If that is not really so, and taking the address this way is not use, it
> should better be expressed in the standard.

No, you are right, I did not recognize the actual problem, because I
concentrated on foo and it's argument, and not on bar().

>> You are not showing us any content of the body of foo(), so we don't
>> know whether foo() might violate the ODR when referring to it's function
>> argument. If foo would be written like this:
>
> This remark quite implies that taking the address to do the function
> call is
> not use, and the body is relevant. IMO that is far from obvious if so.
>
> If I look the code with compiler/code generator's eyes, foo will have code
> identical as if it were foo(const int *). And call to foo will generate
> code
> equivalent to passing&NUM. Resulting different bodies in all TUs. If the
> compiler does not do actual inlining, that is allowed, there's not much
> choice of other ways.

Yep.

>> #include<iostream>
>>
>> const int NUM = 42;
>> inline void foo(const int& arg) { std::cout<< &arg<< std::endl; }
>> inline void bar() { foo(NUM); }
>>
>> it would violate the ODR, yes. The same could happen for an analogously
>> written function bar_2() as follows:
>>
>> #include<iostream>
>>
>> const int NUM_2 = 42;
>> inline void bar_2() { std::cout<< &NUM_2<< std::endl; }
>>
>>> My question is whether this was fixed for C++0x?
>>
>> What do you want to have fixed?
>
> If my original interpretation is correct, then we don't need to use&
> explicitly, still have the ODR on very common cases, especially for
> templates, where taking arguments by const reference is the norm. That
> should not be so.
>
> If the provided interpretation is correct, and inside the function only the
> value is really used without explicit taking of the address, then I'd like
> to have a note or example with code like above stating it shal not worry on
> the user side (and certainly be looked after by implementation).

Unfortunately your problem is real and probably not easy to fix.

> The real world implementations I saw for inline use COMDEFs or __selectany
> for the function body, meaning the code is generated into all objects. Then
> the linker will use a random piece. By ODR they must be sufficiently
> identical, if not it is UB anyway.
>
> I'm thinking of an ill case from the above scenario. Let's change NUM's
> initializer to a function call.

OK, now we have no longer a constant expression, you mean?

> int ret_42(); // defined in a single TU as {return 42;}
> const int NUM = ret_42();
> inline void foo(const int& arg) { std::cout<< arg<< std::endl; }
> inline void bar() { foo(NUM); }
>
> Will bar print always 42? What if it is called before main(), during
> initialization of namespace variables? If every function used its "own"
> body, then NUM is initialized before that and have its value properly.
>
> But if it used the selectany method, the executing body could use
> address of
> NUM from another TU, one that will have that variable initialized only
> after
> my call -- printing 0.
>
> If using the value is what the standard really means, the implementation
> will need some extra measures to have it.

No, unfortunately it really refers to an address and this address is
indirectly used in bar(). Funny things can now also happen e.g. with
non-type templates, because as of C++0x it is now possible that these
are initialized with objects that have internal linkage, so this would
now be allowed to instantiate:

template<const int& R>
struct Ref {
      [..]
};

const int NUM = 42;

inline void bar() { Ref<NUM> r; } // Is now possible...

and it would silently break the ODR similar to your original example.

So, yes, the problem in your original example is real and probably also
quite hard to solve correctly.

Greetings from Bremen,

Daniel Kr   gler


--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Balog Pal" <pasa@lib.hu>
Date: Thu, 28 Apr 2011 12:19:36 CST
Raw View
If I have the following in a header:

const int NUM = 42;
inline void foo(const int &) { ... } // could be template, etc
inline void bar()
{
 foo(NUM);
}

If I understand correctly, by the current standard this will break the one
definition rule, if the header is included in multiple translation units.
Because of the const hack that makes it static, so NUM will really be
different in every TU, therefore bar() will have non-identical definitions.
Placing the program in UB land.

My question is whether this was fixed for C++0x?

--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: =?windows-1252?Q?Daniel_Kr=FCgler?=<daniel.kruegler@googlemail.com>
Date: Thu, 28 Apr 2011 15:46:54 CST
Raw View
Am 28.04.2011 20:19, schrieb Balog Pal:
>
>  If I have the following in a header:
>
>  const int NUM = 42;
>  inline void foo(const int&) { ... } // could be template, etc
>  inline void bar()
>  {
>  foo(NUM);
>  }
>
>  If I understand correctly, by the current standard this will break the one
>  definition rule, if the header is included in multiple translation units.
>  Because of the const hack that makes it static, so NUM will really be
>  different in every TU, therefore bar() will have non-identical definitions.
>  Placing the program in UB land.

I don't see any reason for a concrete violation of the ODR in this
example as *written*. Relevant quotes are from the FDIS (the
corresponding definitions exist for C++03 already and are provided in
parentheses additionally below).

NUM is a variable with internal linkage and it is defined once per
translation unit, so there will be exactly one NUM per translation unit
as it should be.

1) [dcl.stc] p. 7 (p. 6 in C++ 2003):

"A name declared in a namespace scope without a storage-class-specifier
has external linkage unless it has internal linkage because of a
previous declaration and provided it is not declared const. Objects
declared const and not explicitly declared extern have internal linkage."

2) [basic.link] p. 3 (same location in C++ 2003):

"A name having namespace scope (3.3.6) has internal linkage if it is the
name of
[..]
    a variable that is explicitly declared const or constexpr and neither
explicitly declared extern nor previously declared to have external linkage;
[..]"

Both bar and foo are inline function (with external linkage).

See (1)+(2) and see

(3) [basic.def.odr] p. 5 (same location in C++ 2003, but restricted to
integral types and enums at the point where literal types are mentioned):

"There can be more than one definition of a [..] inline function with
external linkage (7.1.2), [..] in a program provided that each
definition appears in a different translation unit, and provided the
definitions satisfy the following requirements. Given such an entity
named D defined in more than one translation unit, then
[..]
    in each definition of D, corresponding names, looked up according to
3.4, shall refer to an entity defined within the definition of D, or
shall refer to the same entity, after overload resolution (13.3) and
after matching of partial template specialization (14.8.3), except that
a name can refer to a const object with internal or no linkage if the
object has the same literal type in all definitions of D, and the object
is initialized with a constant expression (5.19), and the value (but not
the address) of the object is used, and the object has the same value in
all definitions of D;"

You are not showing us any content of the body of foo(), so we don't
know whether foo() might violate the ODR when referring to it's function
argument. If foo would be written like this:

#include<iostream>

const int NUM = 42;
inline void foo(const int&  arg) { std::cout<<  &arg<<  std::endl; }
inline void bar() { foo(NUM); }

it would violate the ODR, yes. The same could happen for an analogously
written function bar_2() as follows:

#include<iostream>

const int NUM_2 = 42;
inline void bar_2() { std::cout<<  &NUM_2<<  std::endl; }

>  My question is whether this was fixed for C++0x?

What do you want to have fixed?

HTH&  Greetings from Bremen,

Daniel Kr   gler




--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]