24

std::piecewise_construct, defined in <utility>, has internal linkage since it is declared constexpr. I wonder if use of std::piecewise_construct in a header can violate ODR. For example:

a.hpp

#include <utility>
#include <tuple>

struct point
{
    point(int x, int y)
      : x(x), y(y)
    {}

    int x, y;
};

inline std::pair<point, point> f(int x1, int y1, int x2, int y2)
{
    return {
        std::piecewise_construct,
        std::forward_as_tuple(x1, y1), std::forward_as_tuple(x2, y2)
    };
}

translation unit 1

#include "a.hpp"

translation unit 2

#include "a.hpp"

The std::piecewise_construct in f in TU 1 refers to a different object than that in f in TU 2. I suspect f violates ODR.

N3290 (probably ISO/IEC 14882:2011 also) says the following case is an exception of ODR, in 3.2/5:

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;

f satisfies almost all the requirements, but "the value (but not the address) of the object is used" seems ambiguous to me. It's true that std::piecewise_construct_t has no state, but a call of the piecewise constructor of std::pair involves a call of the implicit-declared copy constructor of std::piecewise_construct_t, whose argument is const std::piecewise_construct_t &. The address is "used", isn't it?

I'm very puzzled.

Reference: http://lists.boost.org/Archives/boost/2007/06/123353.php

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
iorate
  • 693
  • 5
  • 6

2 Answers2

6

It appears that you already have your answer in that boost mailing list posting. Yes, in my opinion it is undefined behavior or at least not sufficiently clear defined behavior.

See this usenet discussion for the same matter being discussed.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • I also think so. By the way, unlike Boost, there is no need for the standard library to be header-only. Therefore 'piecewise_construct' should have been declared as: `extern const piecewise_construct_t piecewise_construct;`. I wonder why it wasn't. – iorate Oct 03 '11 at 15:08
  • I recommend asking on comp.std.c++ or comp.lang.c++.moderated. – Johannes Schaub - litb Oct 03 '11 at 15:18
  • It's technically illegal but practically extremely harmless. – curiousguy Jul 06 '18 at 22:08
0

IMHO there is no conflict under the ODR.

An unnamed namespace has the same effect as marking things for internal linkage (static). This does indeed mean that every TU uses his own unique definitions for such types/functions.

The way I look at them, how the placeholders (::::_1 and competing flavours) work, is not by instantiation so much as by compiletime type inference:

_1, _2 etc. are merely placeholders, and they don't really need to be compatible (values don't need to be passed from one TU to another, they are passed as type inferred parameters only and as such their actual type is inferred to be have the identity from the current TU).

IOW: You can easily define your own placeholders by specializing some traits, and they should still work like a charm.

namespace boost
{
    template<int I> struct is_placeholder< 
           my_funny_own_placeholder_no_ODR_involved<I> >
    {
        enum _vt { value = I };
    };
}

I suppose the same logic could hold for piecewise_construction (but I haven't looked at that much).

sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    The 'problem' is that when considering both TUs, the respective definitions of `f` are different since they do not refer to the same `std::piecewise_construct` and the address is used, not the value. Thus the potential ODR violation is that `f` could be different in each TU. More information available in the discussion linked by Johannes. – Luc Danton Oct 03 '11 at 12:10
  • @LucDanton: how is that a problem? They are just different functions in different unnamed namespaces? Note that I'm primarily basing this off the linked discussion about the _1, _2... placeholders. I'll read into the linked discussion about piecewise... later – sehe Oct 03 '11 at 12:12
  • `f` is not in an unnamed namespace. If it were, you're correct there would not be a problem. – Luc Danton Oct 03 '11 at 12:14
  • @LucDanton: I see; on reading a bit into that thread, it seems to specifically deal with passing a reference to instances of the type. That _would_ be a problem, yes, since the types are technically not the same (unnamed namespaces are by definition unique). Mmmm. interesting. I will definitely read on that later – sehe Oct 03 '11 at 12:15
  • 2
    The problem isn't the type; `std::piecewise_construct` will have type `std::piecewise_construct_t` in all TU, the same way `const int internal = 42;` will always have type `int`. But the addresses will differ. – Luc Danton Oct 03 '11 at 12:20