私有编译时 -> 运行 时间适配器。奇怪的编译错误
Private compile-time -> run-time adapter. Strange compilation 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;
}
我找不到解释。
为什么说data.code
是函数类型呢?
有可能使这项工作吗?我不希望 data_view
返回为 std::unique_ptr
。
我的期望:
get_view
run_time::data_view
data_view
的部分特化是一个完整类型。
operator()(const DataT &)
是在main::<unnamed struct>
(也就是data
)创建之后特化的,所以也是一个完整类型。
auto
可以推导出:data_view
和DataT
都完整
const run_time::data_view &
是返回的局部结构的 public 基础,因此对象的引用可隐式转换
那么谁能解释它失败的原因以及如何在不诉诸堆分配的情况下使其工作? (run_time::data_view
应该是私有的。我不希望它在 class 或指定适配器 get_view
之外的任何地方使用。
这有效:
template <typename DataT>
void operator()(const DataT &data) const
{
const data_view &view = get_view<DataT, data_view>{}(data);
(*this)(view);
}
我猜它会导致递归,因为从 get_view
推导出来的 auto
不是 const data_view&
类型。所以编译器对调用哪个 operator()(data)
感到困惑。通过显式转换为引用,它变得清晰。
但是,有一个问题是为什么它决定首先使用模板,而不是采用 const 引用的重载。
问题是由函数解析过程中的歧义引起的:
get_view
returns deduced
按值类型是一个很好的候选者
template <typename DataT> operator()(const DataT &)
,从而忽略现有的重载 operator()(const data_view &)
。
在运算符调用之前提供显式转换解决了问题。
template <typename DataT>
void operator()(const DataT &data) const
{
const data_view &view = get_view<DataT, data_view>{}(data);
(*this)(view);
}
基本上,此代码失败并出现一个非常奇怪的错误:
<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;
}
我找不到解释。
为什么说data.code
是函数类型呢?
有可能使这项工作吗?我不希望 data_view
返回为 std::unique_ptr
。
我的期望:
get_view
run_time::data_view
data_view
的部分特化是一个完整类型。operator()(const DataT &)
是在main::<unnamed struct>
(也就是data
)创建之后特化的,所以也是一个完整类型。auto
可以推导出:data_view
和DataT
都完整const run_time::data_view &
是返回的局部结构的 public 基础,因此对象的引用可隐式转换
那么谁能解释它失败的原因以及如何在不诉诸堆分配的情况下使其工作? (run_time::data_view
应该是私有的。我不希望它在 class 或指定适配器 get_view
之外的任何地方使用。
这有效:
template <typename DataT>
void operator()(const DataT &data) const
{
const data_view &view = get_view<DataT, data_view>{}(data);
(*this)(view);
}
我猜它会导致递归,因为从 get_view
推导出来的 auto
不是 const data_view&
类型。所以编译器对调用哪个 operator()(data)
感到困惑。通过显式转换为引用,它变得清晰。
但是,有一个问题是为什么它决定首先使用模板,而不是采用 const 引用的重载。
问题是由函数解析过程中的歧义引起的:
get_view
returns deduced
按值类型是一个很好的候选者
template <typename DataT> operator()(const DataT &)
,从而忽略现有的重载 operator()(const data_view &)
。
在运算符调用之前提供显式转换解决了问题。
template <typename DataT>
void operator()(const DataT &data) const
{
const data_view &view = get_view<DataT, data_view>{}(data);
(*this)(view);
}