11

UPDATE: There is a proposal to change the meaning of auto in certain situations.

Implicit Evaluation of “auto” Variables and Arguments by Joel Falcou and others.

The implicit evaluation shall:

  1. Enable class implementers to indicate that objects of this class are evaluated in an auto statement;
  2. Enable them to determine the type of the evaluated object;

...


C++11's auto keyword is great.

However in my opinion if a type is Not Regular (see for example, What is a "Regular Type" in the context of move semantics?) the usage of auto becomes tricky.

Is there a way to disable the auto declaration for such type?

Suppose one has a ref class that emulates a reference

double 5.;
ref<double> rd = d; // `ref` behaves like a reference, so it is not a regular type
ref<double> rd2 = rd; // `ref` can be (syntactically) copy constructible, (it is not regular for other reason)
auto r = rd; // now r is not `double`, but EVEN WORST it is `ref<double>`.

(in real life it would be a more complicated class, the important point is that the class at hand it is not regular.)

The only way I found auto r = rd not to work (give a compile error) is to make the class non copyable, however I need the class to have copy constructor (with a special semantics, but a copy constructor still).

Is there a way to disable a syntax auto r = rd somehow? when decltype(rd) is not regular.

(Even better could be to be able somehow to tell the compiler what auto should do precisely).

Note: This is not a very artificial problem, one could see that this type of problem is at the core of std::vector<bool>::reference (which is also a reference wrapper). Disabling (somehow) the syntax auto b = v[10] wouldn't solve the problem of std::vector<bool> but it will make bad usage harder.

Am I missing something? Should I change some other part of the design? Should the non-regular classes have a type trait that would help the compiler determine a more general auto (for example deduce bool for auto b = v[10] where std::vector<bool> v.)

alfC
  • 14,261
  • 4
  • 67
  • 118
  • Is adding a `static_assert` where you assign a possibility? – rocambille Nov 30 '16 at 12:26
  • @wasthishelpful, the assignment is not a problem, I can implement the semantics I need in `operator=`, or even delete it. The problem is allowing this `auto r = rd` syntax, and allowing it with the wrong meaning (that it seems impossible to change in C++). The line `auto r = rd` is not an assignment, but a construction with (the wrong) type deduction. (to be more specific, for a "reference-like type" it is (or should be) the binding operation). – alfC Nov 30 '16 at 12:28
  • According to http://www.cplusplus.com/reference/functional/reference_wrapper/ you can access your value by calling `ref.get()`. Constructing from that should be unambiguous. – Adam Hunyadi Nov 30 '16 at 12:45
  • @AdamHunyadi, thanks. My type is not a `reference_wrapper` necessarely (in fact `reference_wrapper` is not what it seem, see http://stackoverflow.com/questions/34235818/type-emulating-a-c-reference-better-than-stdreference-wrapper). `ref` in my example is a type that has the semantics of a reference. In fact I could make the class such that `auto r=static_cast(rd)` or as you say `auto r=rd.get()`, but the problem is that `auto r = rd` still conceptually does the wrong things and it is too easy to be misused. (BTW, `auto b = (bool)v[10]` works, but it not better than `bool b = v[10]`). – alfC Nov 30 '16 at 12:52
  • 3
    Yeah, wouldn't it be nice if C++ had features that would avoid letting you shoot yourself in the foot when it comes to types? But if we're wishing, the ability to disable implicit conversion between signed and unsigned types would be much higher on my list... – Cody Gray - on strike Nov 30 '16 at 13:31
  • 2
    No, there is no way to disable this core language feature. Not until someone writes a coherent proposal and makes all the work necessary to incorporate it into the next C++ standard. `auto` is defined to do a very specific thing, and this thing is exactly the same as what happens in template argument deduction. If you propose to disable `auto` in some cases, you probably should also disable template argument deduction in those same cases, for consistency's sake. – n. m. could be an AI Nov 30 '16 at 14:26
  • You could disable copy for that non regular type. but `auto&&` will still be possible. – Guillaume Racicot Nov 30 '16 at 14:32
  • The meaning of a copy constructor is limited by language rules; among other things, elision of a copy constructor must be logically correct. In general, if you want to have a copy operation that isn't a copy, consider not using the copy constructor. `ref rd2 = rd1.pseudo_copy();`, where `psuedo_copy` returns a type that is also not copyable, but can be used to construct a `ref`. – Yakk - Adam Nevraumont Nov 30 '16 at 15:04

1 Answers1

3

A copy constructor means you expect the class to be copied. auto x = y; does a copy of y into x.

If you want a super-special copy that you don't want to be run automatically, you can use a proxy object.

template <class T>
struct pseudo_copy;

template <class T>
struct pseudo_copy<T const&> {
  T const& t;

  // T const& can be initialized from T&&:
  pseudo_copy(T const& tin) :t(tin) {}
  pseudo_copy(T&& tin): t(tin) {}
  pseudo_copy(pseudo_copy const&) = delete;
};

template <class T>
struct pseudo_copy<T&&> {
  T&& t;
  pseudo_copy(T&& tin): t(std::move(tin)) {}
  pseudo_copy(pseudo_copy const&) = delete;
};

template <class T>
pseudo_copy<T const&> pseudo(T& t) { return {t}; }

template <class T>
pseudo_copy<T&&> pseudo(T&& t) { return {t}; }

struct strange {
  strange(strange const&)=delete;
  strange(pseudo_copy<strange const&>) {} // copy ctor
  strange(pseudo_copy<strange&&>) {} // move ctor
  strange() = default;
};

Now we can:

strange foo() { return pseudo(strange{}); }

strange x = pseudo(foo());

and now every attempt to copy strange must go through a call to pseudo, and use of auto is never legal, because nothing has a copy constructor.

You could also make the copy constructor private, and use it to implement the pseudo copy constructor.


Note that the meaning of copy/move ctor is constrained by elision rules in C++.


In C++17 template class type deduction could make:

template <class T>
struct value{
  value_type_of<T> v;
  value(T in): v(std::forward<T>(in)) {}
};

int x = 3;
value a = std::ref( x );

And a.v would be an int.

underscore_d
  • 6,309
  • 3
  • 38
  • 64
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Well, I disagree, copy-constructible should mean that at least this is valid `decltype(y) x = y` is valid and has some meaning. `auto x = y` is semantically different because in generic code one expects `x` to have value semantics even if `y` doesn't. I am just saying that `auto` apparently is too restrictive. It is a pity that for truly generic code one would need to do something like `typename value_rep_of::type x = y;`. Where `value_rep` is a trait (trivial for most cases, except for non regular types). – alfC Dec 01 '16 at 00:23
  • 2
    @alfc There may be help with template constructor type deduction in C++17 if you accept a wrapper. So `value a=b;` where `value` is a template that stores a value in a field within itself. – Yakk - Adam Nevraumont Dec 01 '16 at 00:26
  • good point, I have to look into that. That is very promising because it hide/abstracts your hack. – alfC Dec 01 '16 at 00:33
  • It turns out that for C++14 the solution I found was to disable the move and copy constructors, this made a lot of sense and forced me to use `auto&&` instead of `auto` but that was ok because at most a class without copy or move constrctor is basically a reference-like type. However technique stopped working in C++17. So I asked this followup question https://stackoverflow.com/questions/58469786/disallow-the-use-of-auto-for-a-given-class-c14-vs-c17-update – alfC Oct 20 '19 at 03:26