On 5 October 2017 at 21:15, Ville Voutilainen <ville.voutilainen@gmail.com> wrote:
On 6 October 2017 at 02:33, Richard Smith <richard@metafoo.co.uk> wrote:
>> > I can't write
>> > NoCopyNorMove x = factory();
>> > in C++14. It's ill-formed. Same goes for
>> > auto x = factory();
>> >
>> >
>> > The macro does not help you with that. It does not magically make a
>> > C++17
>> > feature appear in C++14 mode.
>>
>> I can avoid compiling that when I know it won't work, for which I can
>> use the macro. I can compile that when I know
>> it'll work, for which the macro tells me that it'll work.
>
>
> The thing I'm missing is what alternative you use when the feature is not
> available, and why you would not use that alternative in all cases if you
> want your code to be portable across compilers. Your original example is not
> that, because you can just use the version with
> __cpp_guaranteed_copy_elision not defined, with no loss of functionality.

It's not about loss of functionality, necessary; the C++14 version has
very different design trade-offs:
1) it can only ever be used with factories that are friends
2) it can only ever be used with factories when lifetime-extending
references are used
A probable alternative is to decide that the type is not provided by
the library at all if those trade-offs
aren't palatable.

>> > The choice is that rather than performing tricks with having to
>> > befriend every possible factory function
>> > and forcing users to use lifetime-extending references, such RAII
>> > handles are provided only when copy
>> > elision is available, and otherwise completely different tools need to
>> > be
>> > used.
>> >
>> >
>> > I think we're still missing an example of that.
>>
>> That would be wrapping the whole type in #ifdef
>> __cpp_mandatory_copy_elision, and providing
>> a different type that doesn't rely on copy elision, like a type that
>> is movable and has a different name.
>  This sounds promising. Can you give an example?

I can provide two:

#ifdef __cpp_mandatory_copy_elision
class NoCopyNoMove {...};
#else
/* providing this type in C++14 mode is so icky that we simply don't */

Note that you've been able to do this since C++11:

class NoCopyNoMove {
public:
  NoCopyNoMove(const NoCopyNoMove&) = delete;
  NoCopyNoMove &operator=(const NoCopyNoMove&) = delete;

  NoCopyNoMove(int a, int b, int c);
};

NoCopyNoMove factory(int a) {
  // ...
  return {a, 2, 3}; // constructs return value in place
}

void use() {
  auto &&v = factory();
}

(If you happen to have mandatory copy elision then client code gets to use "auto v = factory();" instead.)

After a while pondering, the best example I've got to demonstrate a need for the feature test macro is something like this:

#ifdef __cpp_mandatory_copy_elision
NoCopyNoMove indirectFactory() {
  return factory(1); // ill-formed prior to C++17
}
#endif

#endif

#ifdef __cpp_mandatory_copy_elision
class NoCopyNoMove {...};
#else
class NoCopyNoMoveTransitional [[deprecated]] {...};
#endif

If you do this, your customers' code breaks when they upgrade their compilers. I would hope/imagine you would instead provide the ...Transitional class unconditionally. That would then make this the same as the previous example.

If copy elision is not available, chances are that there's no reason
to encourage using the alternative
type that has design downsides.

Mandatory copy elision changes what code is valid and what is not. I
find it odd that it's so hard to have a feature
macro for something like that.