Home > Net >  Private compile-time -> run-time adapter. Strange compilation error
Private compile-time -> run-time adapter. Strange compilation error

Time:05-16

Basically, this code fails with a very strange error:

<source>:75:29: error: cannot convert 'get_view<main()::<unnamed struct>, run_time::data_view>::operator()(const main()::<unnamed struct>&) const::View::code' from type 'int (get_view<main()::<unnamed struct>, run_time::data_view>::operator()(const main()::<unnamed struct>&) const::View::)() const' to type 'int'
   75 |                 return data.code;

https://godbolt.org/z/T3h1a7zne

template <typename SourceT, typename ViewT>
struct get_view;

template <typename SourceT>
struct get_view<SourceT, SourceT>
{
    constexpr decltype(auto) operator()(const SourceT &source) const
    {
        return source;
    }
};

// api.h 

#include <iostream>

class compile_time
{
public:

  template <typename DataT>
  void operator()(const DataT &data) const
  {
      std::cout << "compile-time: " << data.code << std::endl;
  }
};

class run_time
{
    // How to make data_view private?
// private:
public:
    class data_view; 

public:

  template <typename DataT>
  void operator()(const DataT &data) const
  {
      (*this)(get_view<DataT, data_view>{}(data));
  }

private:
  void operator()(const data_view &data) const;
};

class run_time::data_view
{
public:

  virtual int code() const = 0;

protected:
  ~data_view() = default; // do not own this 
};


template <typename DataT>
struct get_view<DataT, run_time::data_view>
{
    // don't want to return std::unique_ptr<data_view>
    auto operator()(const DataT &data) const
    {
        struct View : run_time::data_view {
            const DataT& data;

            View(const DataT &data)
              : data(data)
            {}
            
            int code() const override
            {
                return data.code;
            }
        };

        return View(data);
    }
};


// .cpp

void run_time::operator()(const run_time::data_view &dataView) const
{
    std::cout << "run-time: " << dataView.code() << std::endl;
} 

// main.cpp

int main()
{
    struct {
        double irrelevant;
        int code;
    } data{42, 815};

    compile_time{}(data);
    run_time{}(data); // does not compile!!!

    return 0;
}

I can't find an explanation for it. Why does it say that data.code is of a function type?

Is it possible to make this work? I don't want data_view to be returned as std::unique_ptr.

What I expect:

  • Upon get_view partial specialization for run_time::data_view data_view is a complete type.
  • operator()(const DataT &) is specialized after main::<unnamed struct> (which is data) has been created, so it is also a complete type.
  • auto can be deduced: both data_view and DataT are complete
  • const run_time::data_view & is a public base of the returned local struct, so the object's reference is implicitly convertible

So can anyone explain the reason why it fails and how to make it work without resorting to heap allocations? (run_time::data_view should be private. I don't want it to be used anywhere outside the class or designated adapter get_view).


This worked:

  template <typename DataT>
  void operator()(const DataT &data) const
  {
      const data_view &view = get_view<DataT, data_view>{}(data);
      (*this)(view);
  }

I guess it causes a recursion because auto deduced from get_view is not of a type const data_view&. So the compiler gets confused on which operator()(data) to invoke. With explicit conversion to a reference it gets clear.

However, there is a question of why it decides to go with a template first, but not with an overload that takes a const reference.

CodePudding user response:

The problem was caused by an ambiguity during function resolutions: get_view returns a deduced type by value that is a good candidate for template <typename DataT> operator()(const DataT &), thus ignoring the existing overload operator()(const data_view &).

Providing an explicit cast before operator's invocation solves the problem.

  template <typename DataT>
  void operator()(const DataT &data) const
  {
      const data_view &view = get_view<DataT, data_view>{}(data);
      (*this)(view);
  }
  • Related