尝试使用尾随返回类型和标记调度,我编写了以下代码。
#include <string>
#include <iostream>
using namespace std;
namespace Params
{
struct t_param1{};
struct t_param2{};
};
template<typename t_detail>
struct Select;
template<>
struct Select<Params::t_param1> {using choice = Params::t_param1;};
template<>
struct Select<Params::t_param2> {using choice = Params::t_param2;};
class Tester
{
private:
using t_uint32 = uint32_t;
using t_string = string;
private:
t_uint32 m_param1;
// t_string m_param2;
private:
template<typename t_entity>
void assign(const Params::t_param1&, t_entity&& entity);
template<typename t_entity>
void assign(const Params::t_param2&, t_entity&& entity);
auto access(const Params::t_param1&) -> decltype(m_param1);
// auto access(const Params::t_param2&) -> decltype(m_param2);
public:
template<typename t_detail, typename t_entity>
void assign(t_entity&& entity);
template<typename t_detail>
auto access() -> decltype(access(typename Select<t_detail>::choice()));
};
template<typename t_detail, typename t_entity>
void
Tester::assign(t_entity&& entity)
{
assign(typename Select<t_detail>::choice(), entity);
}
template<typename t_entity>
void
Tester::assign(const Params::t_param1&, t_entity&& entity)
{
m_param1 = entity;
cout << "Assigned m_param1 with " << entity << endl;
}
/*
template<typename t_entity>
void
Tester::assign(const Params::t_param2&, t_entity&& entity)
{
m_param2 = entity;
cout << "Assigned m_param2 with " << entity << endl;
}
*/
template<typename t_detail>
auto
Tester::access()
-> decltype(access(typename Select<t_detail>::choice()))
{
return(access(typename Select<t_detail>::choice()));
}
auto
Tester::access(const Params::t_param1&)
-> decltype(m_param1)
{
return(m_param1);
}
/*
auto
Tester::access(const Params::t_param2&)
-> decltype(m_param2)
{
return(m_param2);
}
*/
int main() {
auto tester = Tester();
tester.assign<Params::t_param1>(79);
// tester.assign<Params::t_param2>("viziv");
auto param1 = tester.access<Params::t_param1>();
// auto param2 = tester.access<Params::t_param2>();
cout << "Access: param1 = " << param1 << endl;
// cout << "Access: param2 = " << param2 << endl;
return 0;
}
当我使用Apple LLVM版本7.0.2(clang-700.1.81)编译此代码时,出现以下编译错误
junk1.cpp:78:9: error: out-of-line definition of 'access' does not match any declaration in 'Tester'
Tester::access()
^~~~~~
1 error generated.
奇怪的是,当我取消注释代码以分配和访问param2(在上面的代码中注释掉)时,它编译得很好并产生了所需的结果。
我做错了什么?任何人都可以向我解释为什么包含param2会改变编译行为吗?
答案 0 :(得分:0)
我认为这里有一个半问题。
首先,使用尾随返回类型实质上创建了模板化函数。当您尝试使用类的函数时,类类型不能是不完整的。
这就是为什么将公共access
方法的函数定义移动到类声明中会修复它(Demo);只要尚未定义公共access
方法,该类就不完整,并且在该类完成之前无法定义该方法。
请注意,解决此问题的另一种方法是,access
的私有版本在某种程度上是非成员函数(例如,周围范围内的自由浮动函数)。
这种方法的问题(问题的一半,因为你实际上并没有尝试这样做)是,尝试调用access
的现在自由浮动版本需要编译器评估所有可能的重载,包括公开模板access
(感谢ADL)。当发生这种情况时,Select<t_detail>::choice
将在non-deduced context中进行评估,并且无法获得实际的基础类型。
因此,如果我们都将私有access
移到Tester
之外并将重命名为access2
,那么我们就可以分开了声明和公共access
函数(Demo)