Зачем плюс?
От: B0FEE664  
Дата: 21.11.18 13:49
Оценка: 19 (2) +1 :)
Вопрос из сабжа на засыпку:

#include <chrono>

class A
{
public:
  static const unsigned TIMEOUT = 300;
};


void foo(std::chrono::milliseconds timeout)
{
}


void boo()
{
  foo( std::chrono::milliseconds(+A::TIMEOUT) );
}


Ответ можно найти в С++17 12.2.3.2/3

  Скрытый текст
Что это, как не живительная сила rvalue ссылок?
И каждый день — без права на ошибку...
Re: Зачем плюс?
От: solano  
Дата: 21.11.18 14:38
Оценка:
Здравствуйте, B0FEE664, Вы писали:

Это перегруженный оператор + для каста скопед енума к интегральному типу ?
С наилучшими пожеланиями.
Re: Зачем плюс?
От: rg45 СССР  
Дата: 21.11.18 14:48
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Вопрос из сабжа на засыпку:


Я бы предположил, что в какой-то из реализаций std::chrono::duration что-то перемудрили с конструкторами и теперь приходится форсировано приводить фактические параметры к rvalue выражениям, чтобы выбирался нужный конструктор.


BFE>Ответ можно найти в С++17 12.2.3.2/3


Вот этого не понял. Может у меня уже драфт устарел и пора мне обновляться? У меня в этом пункте вот что:

If a non-volatile non-inline const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression (8.20). The member shall still be defined in a namespace scope if it is odr-used (6.2) in the program and the namespace scope definition shall not contain an initializer. An inline static data member may be defined in the class definition and may specify a brace-or-equal-initializer. If the member is declared with the constexpr specifier, it may be redeclared in namespace scope with no initializer (this usage is deprecated; see D.1). Declarations of other static data members shall not specify a brace-or-equal-initializer.

--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Зачем плюс?
От: σ  
Дата: 21.11.18 15:02
Оценка: 21 (2) +1
BFE>>Ответ можно найти в С++17 12.2.3.2/3

R>Вот этого не понял. Может у меня уже драфт устарел и пора мне обновляться? У меня в этом пункте вот что:


R>

R>If a non-volatile non-inline const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression (8.20). The member shall still be defined in a namespace scope if it is odr-used (6.2) in the program and the namespace scope definition shall not contain an initializer. An inline static data member may be defined in the class definition and may specify a brace-or-equal-initializer. If the member is declared with the constexpr specifier, it may be redeclared in namespace scope with no initializer (this usage is deprecated; see D.1). Declarations of other static data members shall not specify a brace-or-equal-initializer.


Байнд ссылки (в параметрах конструктора) напрямую к A::TIMEOUT это odr-use.
А lvalue-to-rvalue conversion в вычислении выражения `+A::TIMEOUT` это не odr-use.
Re: Зачем плюс?
От: T4r4sB Россия  
Дата: 21.11.18 15:19
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Вопрос из сабжа на засыпку:


Да всё просто: A::TIMEOUT — это имя старой переменной, её нельзя рвалуить, а +A::TIMEOUT — это выражение, вычисляющее новую переменную, которую можно мувить и рвалуить.
Re[2]: Зачем плюс?
От: B0FEE664  
Дата: 21.11.18 16:41
Оценка: 1 (1)
Здравствуйте, rg45, Вы писали:

R>Я бы предположил, что в какой-то из реализаций std::chrono::duration что-то перемудрили с конструкторами и теперь приходится форсировано приводить фактические параметры к rvalue выражениям, чтобы выбирался нужный конструктор.


конструктор такой:
template<class Rep2>
constexpr explicit duration (const Rep2& n);


BFE>>Ответ можно найти в С++17 12.2.3.2/3

R>Вот этого не понял. Может у меня уже драфт устарел и пора мне обновляться? У меня в этом пункте вот что:
Выделил:
R>

R>If a non-volatile non-inline const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression (8.20). The member shall still be defined in a namespace scope if it is odr-used (6.2) in the program and the namespace scope definition shall not contain an initializer. An inline static data member may be defined in the class definition and may specify a brace-or-equal-initializer. If the member is declared with the constexpr specifier, it may be redeclared in namespace scope with no initializer (this usage is deprecated; see D.1). Declarations of other static data members shall not specify a brace-or-equal-initializer.


Это значит, что надо
либо добавить строку вне класса:
//static
const unsigned A::TIMEOUT;

либо переписать
void boo()
{
  auto t = A::TIMEOUT;
  foo( std::chrono::milliseconds(t) );
}

либо можно просто добавить '+' и тогда по ссылке будет передаваться не сам объект, а его копия.
И каждый день — без права на ошибку...
Re[3]: Зачем плюс?
От: watchmaker  
Дата: 21.11.18 17:11
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:


BFE>Это значит, что надо

BFE>либо добавить
BFE>либо переписать
BFE>либо

А что вариант с inline не рассматривается? Он самый простой же:
class A
{
public:
  static inline    unsigned TIMEOUT = 300;
  // или 
  static constexpr unsigned TIMEOUT = 300;
};


Теперь никаких дополнительных определений писать не нужно, и можно использовать A::TIMEOUT в любом контексте.
Re: Зачем плюс?
От: σ  
Дата: 21.11.18 18:13
Оценка:
BFE>
  Скрытый текст
BFE>Что это, как не живительная сила rvalue ссылок?

А они тут при чём?
Re[4]: Зачем плюс?
От: B0FEE664  
Дата: 22.11.18 09:42
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>А что вариант с inline не рассматривается?

Бываю такие конторы, которые только-только начали переходить на С++11...
И каждый день — без права на ошибку...
Re[2]: Зачем плюс?
От: B0FEE664  
Дата: 22.11.18 09:44
Оценка:
Здравствуйте, σ, Вы писали:

BFE>>
  Скрытый текст
BFE>>Что это, как не живительная сила rvalue ссылок?

σ>А они тут при чём?

А разве это не rvalue ссылка которая преобразуется к обычной ссылке?
И каждый день — без права на ошибку...
Re[3]: Зачем плюс?
От: rg45 СССР  
Дата: 22.11.18 09:56
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>>>Что это, как не живительная сила rvalue ссылок?

σ>>А они тут при чём?

BFE>А разве это не rvalue ссылка которая преобразуется к обычной ссылке?


Правильнее сказать, rvalue выражение, которое материализуется во временный объект, который в свою очередь биндится к константной lvalue ссылке. Обычная схема, существовавшая еще до появления rvalue ссылок.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 22.11.2018 9:58 rg45 . Предыдущая версия .
Re[4]: Зачем плюс?
От: B0FEE664  
Дата: 22.11.18 10:26
Оценка:
Здравствуйте, rg45, Вы писали:

R>Правильнее сказать, rvalue выражение, которое материализуется во временный объект, который в свою очередь биндится к константной lvalue ссылке. Обычная схема, существовавшая еще до появления rvalue ссылок.


Разве по стандарту 98 можно было передать временный объект по ссылке? (Помню, что MS компилятор отходил от стандарта в этом случае)
И каждый день — без права на ошибку...
Re[5]: Зачем плюс?
От: rg45 СССР  
Дата: 22.11.18 10:29
Оценка:
Здравствуйте, B0FEE664, Вы писали:

R>>Правильнее сказать, rvalue выражение, которое материализуется во временный объект, который в свою очередь биндится к константной lvalue ссылке. Обычная схема, существовавшая еще до появления rvalue ссылок.


BFE>Разве по стандарту 98 можно было передать временный объект по ссылке? (Помню, что MS компилятор отходил от стандарта в этом случае)


Ну в C++03 то уж точно можно было.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: Зачем плюс?
От: rg45 СССР  
Дата: 22.11.18 10:38
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>>Разве по стандарту 98 можно было передать временный объект по ссылке? (Помню, что MS компилятор отходил от стандарта в этом случае)


R>Ну в C++03 то уж точно можно было.


И в 98 тоже:

8.5.3 References
/5
. . .
— If the initializer expression is an rvalue, with T2 a class type, and “cv1 T1” is referencecompatible with “cv2 T2,” the reference is bound in one of the following ways (the choice is implementationdefined):
— The reference is bound to the object represented by the rvalue (see 3.10) or to a subobject within that object.
— A temporary of type “cv1 T2” [sic] is created, and a constructor is called to copy the entire rvalue object into the temporary. The reference is bound to the temporary or to a subobject within the temporary.93)

--
Не можешь достичь желаемого — пожелай достигнутого.
Re: Зачем плюс?
От: Кодт Россия  
Дата: 22.11.18 15:01
Оценка:
Здравствуйте, B0FEE664, Вы писали:

Зачем плюс? Незачем, это избыточный код.

BFE>Что это, как не живительная сила rvalue ссылок?


Это живительная сила explicit конструктора.
constexpr duration() = default;

duration( const duration& ) = default;

template< class Rep2 >
constexpr explicit duration( const Rep2& r );

template< class Rep2, class Period2 >
constexpr duration( const duration<Rep2,Period2>& d );



https://ideone.com/qz7xwr
#include <iostream>
#include <chrono>
using namespace std;
 
template<class T>
struct C {
    C() {}
    template<class U> C(const C<U>&) {}
    template<typename T2> constexpr explicit C(const T2& t) {}  // у chrono::duration там ещё enable_if, но для пары (unsigned, long) он выполняется
};
 
using Z = chrono::milliseconds;
using S = C<unsigned long>;
 
void foo(Z) {}
void bar(S) {}
 
static const unsigned t1 = 1;
int main() {
    foo(Z(t1));  // foo(t1); foo(1L);
    bar(S(t1));  // bar(t2); bar(1L);
}
Перекуём баги на фичи!
Re[2]: Зачем плюс?
От: B0FEE664  
Дата: 22.11.18 15:17
Оценка: 34 (1) +1
Здравствуйте, Кодт, Вы писали:

  Скрытый текст
К>Зачем плюс? Незачем, это избыточный код.

BFE>>Что это, как не живительная сила rvalue ссылок?


К>Это живительная сила explicit конструктора.

К>
К>constexpr duration() = default;

К>duration( const duration& ) = default;

К>template< class Rep2 >
К>constexpr explicit duration( const Rep2& r );

К>template< class Rep2, class Period2 >
К>constexpr duration( const duration<Rep2,Period2>& d );
К>



К>https://ideone.com/qz7xwr

К>
К>#include <iostream>
К>#include <chrono>
К>using namespace std;
 
К>template<class T>
К>struct C {
К>    C() {}
К>    template<class U> C(const C<U>&) {}
К>    template<typename T2> constexpr explicit C(const T2& t) {}  // у chrono::duration там ещё enable_if, но для пары (unsigned, long) он выполняется
К>};
 
К>using Z = chrono::milliseconds;
К>using S = C<unsigned long>;
 
К>void foo(Z) {}
К>void bar(S) {}
 
К>static const unsigned t1 = 1;
К>int main() {
К>    foo(Z(t1));  // foo(t1); foo(1L);
К>    bar(S(t1));  // bar(t2); bar(1L);
К>}
К>

По условию
static const unsigned t1 = 1;

должна быть внутри класса.
И тогда не собирается.
И каждый день — без права на ошибку...
Re[6]: Зачем плюс?
От: B0FEE664  
Дата: 22.11.18 15:28
Оценка: +1
Здравствуйте, rg45, Вы писали:

BFE>>>Разве по стандарту 98 можно было передать временный объект по ссылке? (Помню, что MS компилятор отходил от стандарта в этом случае)

R>>Ну в C++03 то уж точно можно было.
R>И в 98 тоже:

Хмм. Действительно. Для константных ссылок так было всегда. Значит я ошибся и rvalue ни при чём.
И каждый день — без права на ошибку...
Re[3]: Зачем плюс?
От: Кодт Россия  
Дата: 22.11.18 15:55
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>По условию

BFE>
BFE>static const unsigned t1 = 1;
BFE>

BFE>должна быть внутри класса.
BFE>И тогда не собирается.

Хз, это какие-то заморочки компилятора. Ideone же собирает. Даже если это член класса.
(Ровно до того момента, пока мы не попытаемся явно взять адрес этой константы).

https://ideone.com/qz7xwr — удача
https://ideone.com/kDksiR — неудача — ошибка линковки
Перекуём баги на фичи!
Re[2]: Зачем плюс?
От: rg45 СССР  
Дата: 22.11.18 15:56
Оценка: 62 (5) +1 :)
Здравствуйте, Кодт, Вы писали:

К>Зачем плюс? Незачем, это избыточный код.

BFE>>Что это, как не живительная сила rvalue ссылок?
К>Это живительная сила explicit конструктора.

Как мне кажется, здесь избыточная сложность примера мешает взглянуть в корень. Упрощаем, насколько это возможно. Начинаем с работающего варианта:

https://wandbox.org/permlink/XSlvNSjYEOCapn2o

struct A {
  static const int value = 42;
};

void foo(const int&) {}

int main() {
  foo(+A::value);
}


Стоит убрать "плюсик", как начинает валиться с ошибкой: "undefined reference to `A::value'". А происходит это потому, что выражения "A::value" и "+A::value" имеют разные типы: первое — lvalue, второе — rvalue. Использование первого выражения рассматривается как odr-used и требует, согласно 12.2.3.2/3, чтобы статический член был определен в пространстве имен.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[3]: Зачем плюс?
От: σ  
Дата: 22.11.18 16:35
Оценка: +1 :)
Здравствуйте, B0FEE664, Вы писали:

BFE>Здравствуйте, σ, Вы писали:


BFE>>>Что это, как не живительная сила rvalue ссылок?

σ>>А они тут при чём?

BFE>А разве это не rvalue ссылка которая преобразуется к обычной ссылке?


То чувство, когда притащил код с демонстрацией тонкостей плюсов, но не отличаешь Гоголя от Гегеля rvalue от rvalue reference
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.