C++涵盖 const 和非常量方法的模板
我遇到了 const
和非 const
版本重复相同代码的问题。我可以用一些代码来说明这个问题。这里有两个示例访问者,一种修改访问的对象,另一种则不修改。
struct VisitorRead
{
template <class T>
void operator()(T &t) { std::cin >> t; }
};
struct VisitorWrite
{
template <class T>
void operator()(const T &t) { std::cout << t << "\n"; }
};
现在这里是一个聚合对象 - 它只有两个数据成员,但我的实际代码要复杂得多:
struct Aggregate
{
int i;
double d;
template <class Visitor>
void operator()(Visitor &v)
{
v(i);
v(d);
}
template <class Visitor>
void operator()(Visitor &v) const
{
v(i);
v(d);
}
};
还有一个演示上述内容的函数:
static void test()
{
Aggregate a;
a(VisitorRead());
const Aggregate b(a);
b(VisitorWrite());
}
现在,这里的问题是 Aggregate::operator() 的重复code> 用于 const
和非 const
版本。
是否可以通过某种方式避免重复这段代码?
我有一个解决方案:
template <class Visitor, class Struct>
void visit(Visitor &v, Struct &s)
{
v(s.i);
v(s.i);
}
static void test2()
{
Aggregate a;
visit(VisitorRead(), a);
const Aggregate b(a);
visit(VisitorWrite(), b);
}
这意味着不需要 Aggregate::operator() ,并且没有重复。但我对 visit()
是通用的而没有提及 Aggregate
类型这一事实感到不满意。
有更好的办法吗?
I have a problem with duplication of identical code for const
and non-const
versions. I can illustrate the problem with some code. Here are two sample visitors, one which modifies the visited objects and one which does not.
struct VisitorRead
{
template <class T>
void operator()(T &t) { std::cin >> t; }
};
struct VisitorWrite
{
template <class T>
void operator()(const T &t) { std::cout << t << "\n"; }
};
Now here is an aggregate object - this has just two data members but my actual code is much more complex:
struct Aggregate
{
int i;
double d;
template <class Visitor>
void operator()(Visitor &v)
{
v(i);
v(d);
}
template <class Visitor>
void operator()(Visitor &v) const
{
v(i);
v(d);
}
};
And a function to demonstrate the above:
static void test()
{
Aggregate a;
a(VisitorRead());
const Aggregate b(a);
b(VisitorWrite());
}
Now, the problem here is the duplication of Aggregate::operator()
for const
and non-const
versions.
Is it somehow possible to avoid duplication of this code?
I have one solution which is this:
template <class Visitor, class Struct>
void visit(Visitor &v, Struct &s)
{
v(s.i);
v(s.i);
}
static void test2()
{
Aggregate a;
visit(VisitorRead(), a);
const Aggregate b(a);
visit(VisitorWrite(), b);
}
This means neither Aggregate::operator()
is needed and there is no duplication. But I am not comfortable with the fact that visit()
is generic with no mention of type Aggregate
.
Is there a better way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我倾向于喜欢简单的解决方案,因此我会选择自由函数方法,可能会添加 SFINAE 来禁用除
Aggregate
之外的类型的函数:其中
enable_if
,is_same 和
remove_const
实际上很容易实现编辑: 写作时SFINAE 方法 我意识到在 OP 中提供普通模板化(无 SFINAE)解决方案存在相当多的问题,其中包括如果您需要提供多个可访问类型,则不同的模板会发生冲突(即它们与其他模板一样匹配)。通过提供 SFINAE,您实际上只为满足条件的类型提供
visit
函数,将奇怪的 SFINAE 转换为等价的:I tend to like simple solutions, so I would go for the free-function approach, possibly adding SFINAE to disable the function for types other than
Aggregate
:Where
enable_if
,is_same
andremove_const
are actually simple to implement if you don't have a C++0x enabled compiler (or you can borrow them from boost type_traits)EDIT: While writing the SFINAE approach I realized that there are quite a few problems in providing the plain templated (no SFINAE) solution in the OP, which include the fact that if you need to provide more than one visitable types, the different templates would collide (i.e. they would be as good a match as the others). By providing SFINAE you are actually providing the
visit
function only for the types that fulfill the condition, transforming the weird SFINAE into an equivalent to:好的,仍然有一些样板文件,但没有依赖于聚合实际成员的代码重复。与(例如)Scott Meyers 提倡的避免 getter 重复的
const_cast
方法不同,编译器将确保两个公共函数的 const 正确性。OK, so there's still some boilerplate, but no duplication of the code that depends on the actual members of the Aggregate. And unlike the
const_cast
approach advocated by (e.g.) Scott Meyers to avoid duplication in getters, the compiler will ensure the const-correctness of both public functions.由于您的最终实现并不总是相同,因此我认为对于您所感知的“问题”没有真正的解决方案。
让我们想一想。我们必须满足
Aggregate
为const 或非const 的情况。当然我们不应该放松这一点(例如仅提供非常量版本)。现在,运算符的 const 版本只能调用通过 const-ref (或按值)获取参数的访问者,而非常量版本可以调用任何访问者。
您可能认为可以用两个实现之一替换另一个实现。为此,您始终会根据非常量版本来实现 const 版本,而不是相反。假设:
但为了使这一点有意义,第 2 行要求该操作在逻辑上是不可变的。例如,在典型的成员访问运算符中,这是可能的,您可以在其中提供对某些元素的常量或非常量引用。但在您的情况下,您无法保证
operator()(v)
调用在*this
上不会发生变化!因此,您的两个功能确实相当不同,尽管它们在形式上看起来很相似。你不能用一个来表达另一个。
也许您可以从另一种角度看待这一点:您的两个功能实际上并不相同。在伪代码中,它们是:
实际上,想想看,也许如果您愿意稍微修改一下签名,就可以做一些事情:
Since your ultimate implementations are not always identical, I don't think there's a real solution for your perceived "problem".
Let's think about this. We have to cater for the situations where
Aggregate
is either const or non-const. Surely we should not relax that (e.g. by providing only a non-const version).Now, the const-version of the operator can only call visitors which take their argument by const-ref (or by value), while the non-constant version can call any visitor.
You might think that you can replace one of the two implementations by the other. To do so, you would always implement the const version in terms of the non-const one, never the other way around. Hypothetically:
But for this to make sense, line #2 requires that the operation is logically non-mutating. This is possible for example in the typical member-access operator, where you provide either a constant or a non-constant reference to some element. But in your situation, you cannot guarantee that the
operator()(v)
call is non-mutating on*this
!Therefore, your two functions are really rather different, even though they look formally similar. You cannot express one in terms of the other.
Maybe you can see this another way: Your two functions aren't actually the same. In pseudo-code, they are:
Actually, coming to think of it, perhaps if you're willing to modify the signature a bit, something can be done:
通常对于这种类型的事情,最好使用有意义的方法。例如,
load()
和save()
。他们说了一些关于将通过访问者执行的操作的具体内容。通常会提供 const 和非 const 版本(无论如何,对于访问器之类的东西),因此它看起来只是重复,但可以在以后为您节省一些令人头痛的调试。如果您确实想要一个解决方法(我不建议这样做),那就声明方法const
,并将所有成员mutable
声明。Normally with this type of thing, it's possibly better to use methods that make sense. For example,
load()
andsave()
. They say something specific about the operation that is to be carried out via the visitor. Typically both a const and non-const version is provided (for things like accessors anyway), so it only appears to be duplication, but could save you some headache debugging later down the line. If you really wanted a workaround (which I wouldn't advice), is to declare the methodconst
, and all the membersmutable
.添加访问者特征来判断它是否正在修改(常量或非常量使用)。
这是由 STL 迭代器使用的。
Add visitor trait to tell whether it's modifying or not (const or non-const use).
This is used by STL iterators.
您可以使用 const_cast 并更改 VisitorRead 的方法签名,以便它也采用 const T&作为参数,但我认为这是一个丑陋的解决方案。
You could use const_cast and change VisitorRead's method signature so it also take's const T& as a parameter, but I think that is an ugly solution.
另一个解决方案 - 要求
Visitor
类具有一个在应用时添加const
的元函数:Another solution - require the
Visitor
class to have a metafunction that addsconst
when it applies: