c++autoreinterpret-castnullptrstdoptional

C++ compiler says "inconsistent deduction for auto return type"


There is this nice feature in C++ where you can say the function has return type "auto" and compiler will figure it out. However, what if I return a pointer and nullptr on error? Somehow compiler then fails to deduce the correct type and gives an error.

In the following simple example imagine std::vector<int> is scheduled to be replaced by something else completely in future, to justify the use of auto here:

#include<vector>
std::vector<int> e;
auto test(){
  if(!e.empty())
    return &e[0];
  return nullptr;
}

In c++17 I get above error message.

So I tried replacing last return by

return reinterpret_cast<decltype(&e[0])>(nullptr)

and get the error invalid cast. Only solution I see is replacing that return by the 3 lines:

auto out=&e[0];
out=nullptr;
return out;

I probably could reduce that to 2 lines by replacing auto with some kind of decltype, but I imagine some other kind of cast could do what I want in a single line? Or do I need to use a newer version of the c++ standard for that case?

I also tried std::make_optional and get the same problem with nullopt_t being different type than std::optional. What I really would love is if compiler would automatically deduce type to be std::optional anyway...


Solution

  • right, nullptr is type std::nullptr_t which is not type int*.

    Static cast should be ok.

    #include<vector>
    
    std::vector<int> e;
    
    auto test(){
      if(!e.empty())
        return &e[0];
      return static_cast<decltype(&e[0])>(nullptr);
    }
    

    https://godbolt.org/z/sqfnqd69q

    Even if reinterpret_cast worked (it doesn't), it would be overkill.


    Regarding your follow up question, not sure what you want to achieve, but this seems close:

    #include<vector>
    #include<optional>
    
    std::vector<int> e;
    
    auto test(){
    //  if(!e.empty())
    //    return std::make_optional(e[0]);
    //  return decltype(std::make_optional(e[0]))(std::nullopt);
        return e.empty()?std::nullopt:std::optional{e[0]};
    }
    

    I think this is more idiomatic:

    #include<vector>
    
    std::vector<int> e;
    
    auto test(){
      if(!e.empty())
        return e.data();
      return static_cast<std::vector<int>::pointer>(nullptr);
    }
    

    I am a fan of auto, but this is one of the cases is better not to use it:

    #include<vector>
    
    std::vector<int> e;
    
    auto test() -> std::vector<int>::pointer  // int*
    {  
      if(!e.empty())
        return e.data();
      return nullptr;
    }