Home > other >  decltype evaluating the wrong type from an expression list
decltype evaluating the wrong type from an expression list

Time:02-02

while experimenting with the answer from This post, ended up with the following piece of code:

#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;
}

which yields:

d

Here, d comes from the comma operator. However, as the last expression inside the decltype specifier is std::true_type{}, why does the decltype specifier resolves to the type returned by the comma operator instead of std::true type.

My Theory is that std::true_type can be implicitly cast into an int, which operator,() here takes as a parameter, hence the decltype expression is equivalent to:

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

Can you confirm this is correct?

What i am planning on doing when i will have a better grasp of the compiler's behavior, i plan on replacing the contents of the decltype specifier with:

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

which will prevent operator,() from matching, even if it is overridden. Can you also confirm this is correct?

CodePudding user response:

I added a memebr to MyPtr to get more inforamtion. Then actually evaluating the expression tells you what happens:

#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{};
}

Output:

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

Your "Theory" is correct. Lets remove the and * to make it simpler, then you can think of f1,f2,std::true_type{} as f1.operator,( f2.operator,(std::true_type{}). std::true_type has operator bool and bool can be implcicitly converted to int. To illustrate that I also added the example with false_type in the code.

The operator bool can be found here as operator value_type, because std::true_type is just an alias for std::integral_constant<bool,true>.


which will prevent operator,() from matching, even if it is overridden. Can you also confirm this is correct?

Yes. It is not possible to implement an comma operator overload for void.

  • Related