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 forrun_time::data_view
data_view
is a complete type. operator()(const DataT &)
is specialized aftermain::<unnamed struct>
(which isdata
) has been created, so it is also a complete type.auto
can be deduced: bothdata_view
andDataT
are completeconst 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);
}