Decltype оценивает неверный тип из списка выражений

экспериментируя с ответом от Этот пост, в итоге получился следующий кусок кода:

#include <iostream>
#include <typeinfo>
 
 namespace Test
 {
    struct MyPtr
    {
        double operator, (int Input)
        {
            std::cout << "Operator, called" << std::endl;
            return 1.0;
        }

        MyPtr operator* ()
        {
            std::cout << "operator* called" << std::endl;
            return *this ;
        }

        MyPtr operator++ ()
        {
            std::cout << "operator++ called" << std::endl;
            return *this;
        }

        MyPtr operator!= (const MyPtr& Other)
        {
            std::cout << "operator!= called" << std::endl;
            return *this;
        }
    };

    struct Foo
    {
        MyPtr Ptr;
    };

    MyPtr begin(Foo& t)
    {
        return t.Ptr;
    }

    MyPtr end(Foo& t)
    {
        return t.Ptr;
    }
 }
 
int main()
{
    std::cout << typeid(decltype(++begin(std::declval<Test::Foo&>()),
                                 *begin(std::declval<Test::Foo&>()), 
                                  std::true_type{})).name() <<std::endl;
}

который дает:

d

Здесь d происходит от оператора запятой. Однако, поскольку последним выражением в спецификаторе decltype является std::true_type{}, почему спецификатор decltype разрешается в тип, возвращаемый оператором запятой, а не в тип std::true.

Моя теория заключается в том, что std::true_type можно неявно привести к типу int, который оператор () здесь принимает в качестве параметра, поэтому выражение decltype эквивалентно:

    std::cout << typeid(decltype(++begin(std::declval<Test::Foo&>()),
                                 declval<double&>())).name() <<std::endl;

Вы можете подтвердить, что это правильно?

Что я планирую делать, когда лучше разберусь в поведении компилятора, я планирую заменить содержимое спецификатора decltype на:

    std::cout << typeid(decltype(void(++begin(std::declval<Test::Foo&>())),
                                 void(*begin(std::declval<Test::Foo&>())), 
                                  std::true_type{})).name() <<std::endl;

что предотвратит сопоставление оператора(), даже если он будет переопределен. Можете ли вы также подтвердить, что это правильно?

вы могли бы легче увидеть, что происходит, фактически оценивая выражения, а не используя их только в невычисленном контексте

463035818_is_not_a_number 31.01.2023 13:49

Как то Демо.

Jarod42 31.01.2023 13:51

если одним операндом является void(..), то оператор запятой, предоставленный пользователем, действительно не используется.

Jarod42 31.01.2023 13:53
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
3
77
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я добавил участника в MyPtr, чтобы получить больше информации. Затем фактическая оценка выражения говорит вам, что происходит:

#include <iostream>
#include <typeinfo>

struct MyPtr {
    int value;
    MyPtr(int x) : value(x) {}

    double operator,(int Input) {
        std::cout << "Operator, called value = " << value
                  << " Input = " << Input << std::endl;
        return 1.0;
    }
    MyPtr operator*() {
        std::cout << "operator* called value = " << value << std::endl;
        return *this;
    }
    MyPtr operator++() {
        std::cout << "operator++ called value = " << value << std::endl;
        return *this;
    }
    MyPtr operator!=(const MyPtr& Other) {
        std::cout << "operator!= called" << std::endl;
        return *this;
    }
};

struct Foo { MyPtr Ptr; };

MyPtr begin(Foo& t) { return t.Ptr; }

MyPtr end(Foo& t) { return t.Ptr; }

int main() {
    Foo f1{10};
    Foo f2{20};
    ++begin(f1), *begin(f2), std::true_type{};
    ++begin(f1), *begin(f2), std::false_type{};
}

Вывод:

operator++ called value = 10
operator* called value = 20
Operator, called value = 20 Input = 1
operator++ called value = 10
operator* called value = 20
Operator, called value = 20 Input = 0

Ваша "Теория" верна. Давайте удалим ++ и *, чтобы было проще, тогда вы можете думать о f1,f2,std::true_type{} как о f1.operator,( f2.operator,(std::true_type{}). std::true_type имеет operator bool и bool может быть неявно преобразовано в int. Чтобы проиллюстрировать это, я также добавил в код пример с false_type.

operator bool можно найти здесь как operator value_type, потому что std::true_type — это просто псевдоним для std::integral_constant<bool,true>.


что предотвратит сопоставление оператора(), даже если он будет переопределен. Можете ли вы также подтвердить, что это правильно?

Да. Невозможно реализовать перегрузку оператора запятой для void.

*begin и ++begin здесь, чтобы (в конечном итоге) создать трейт для проверки, поддерживает ли тип итераторы. Я согласен, что они лишние для этого вопроса. Однако как в вашем ответе получается, что MyPtr{},MyPtr{} является двойным? Оператор запятой ожидает int, а не MyPtr. Кроме того, вы правы, оцениваемые контексты проще в использовании.

J.M 31.01.2023 14:03

Другие вопросы по теме