Is there a way to disable auto declaration for non regular types?
Asked Answered
S

1

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.)

Stairhead answered 30/11, 2016 at 12:16 Comment(8)
Is adding a static_assert where you assign a possibility?Imbibe
@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).Stairhead
According to cplusplus.com/reference/functional/reference_wrapper you can access your value by calling ref.get(). Constructing from that should be unambiguous.Janina
@AdamHunyadi, thanks. My type is not a reference_wrapper necessarely (in fact reference_wrapper is not what it seem, see #34236318). 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<double>(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]).Stairhead
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...Depravity
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.Statistician
You could disable copy for that non regular type. but auto&& will still be possible.Printer
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<double> rd2 = rd1.pseudo_copy();, where psuedo_copy returns a type that is also not copyable, but can be used to construct a ref<double>.Yawn
Y
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.

Yawn answered 30/11, 2016 at 15:30 Comment(4)
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<decltype(y)>::type x = y;. Where value_rep is a trait (trivial for most cases, except for non regular types).Stairhead
@alfc There may be help with template constructor type deduction in C++17 if you accept a wrapper. So value a=b; where value<T> is a template that stores a value in a field within itself.Yawn
good point, I have to look into that. That is very promising because it hide/abstracts your hack.Stairhead
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 #58470286Stairhead

© 2022 - 2024 — McMap. All rights reserved.