【问题标题】:Is it possible to parameterize the constness of a templated member function?是否可以参数化模板化成员函数的常量?
【发布时间】:2017-02-28 04:58:56
【问题描述】:

除了函数名本身之外,模板使函数签名的大部分内容都可参数化。但是是否也可以参数化成员函数的常量?

琐碎、简约、非模板示例:

struct Foo {
    Foo *       self()       { return this; }
    Foo const * self() const { return this; }
};

vs 稻草人模板化假设

struct Foo {
    template<typename T> T self() std::constness_of(T) { return this; }
};

【问题讨论】:

  • 您的第二个问题应该被删除。可能有,但我怀疑有人能证明这一点。
  • const(expr) 会很好。连同noexcept(auto)
  • std-proposals 有一些关于此的主题(拼写为 auto_const 或类似名称)。不过没有最近的实际论文。
  • @T.c.我认为const(expr) 是一个更好的系统,const(auto) 做了一些黑魔法(它成为这种类型的模板函数?或者它自动编写两个重载?)
  • 当第一个参数的名称是this 时,我想要这种特殊行为。在这种情况下,this 可以实现为转发引用。 template&lt;typename T&gt; method(T&amp;&amp; this, .. other args ..)T 会告诉你 cv 限定符,以及它是左值还是右值

标签: c++ c++11 templates constants c++14


【解决方案1】:

不,但解决方法很简单,而且可能更具可读性,目的很明确:

struct Foo {
    template<typename T>
    std::enable_if_t<std::is_const<T>::value,T> self() const { return this; }
    template<typename T>
    std::enable_if_t<!std::is_const<T>::value,T> self() { return this; }
};

【讨论】:

  • 可能不太清楚,但目标是随意自定义函数的常量性,而不需要在不需要的地方重复代码。在极简主义示例中,重新实现是微不足道的,但在许多情况下并非如此。
  • 这显然比 OP 的代码可读性差……然后你必须在任何地方都调用x.self() 而不是调用x.self&lt;decltype(x)&gt;()。 -1.
【解决方案2】:

但是是否也可以参数化成员函数的常量?

不,你不能。您无法在函数签名中访问 this 指向的隐式对象,因此您不能以任何方式对其进行调度或模板化。成员函数上的 cv 限定符必须拼写出来。

对于更复杂的成员函数,您可以让一个调用另一个(通常是非const 调用const 来避免UB)以避免一些代码重复。


或者你总是可以写一个非会员friend

struct Foo {
    template <class T,
        std::enable_if_t<std::is_base_of<Foo, std::decay_t<T>>::value>* = nullptr
        >
    friend T* self(T& x) { return &x; }
};

我们需要 SFINAE 来确保不会为 Wrapper&lt;Foo&gt; 等意外类型找到 self()。请注意,这比您的原始代码要长得多,因此仅在具有复杂逻辑的上下文中才有意义。

如果采用 UFCS 肯定会很有趣,现在我们都通过非成员 friends 编写 const/non-const 重载,我们仍然像成员一样调用它们。

【讨论】:

  • 关于“您无法在函数签名中访问 this 指向的隐式对象”,这在技术上是正确的,但这只是因为函数签名不包含返回类型。 IE。虽然关于模板化成员函数的 constness(不能)的结论是正确的,但推理不是。
  • @Cheersandhth.-Alf 不是你需要的返回类型...返回类型是基于this的cv-qualification。
  • 对,结论是正确的。但是对于函数type 的推理(我引用的那一点)是错误的:对于函数signature,它只是字面上正确的。或者也许我很笨,你的意思是非常精确,但是请为读者澄清签名和类型之间的区别(我认为大多数 SO 读者不会知道)。
【解决方案3】:

a comment 的另一个答案中,您澄清了这一点

目标是随意自定义函数的常量性,而不需要在不需要的地方重复代码

以下是一种可能性,用模板化的static 成员函数表示成员函数的const 和非const 版本。

对于更一般的情况,需要转发参数。

两种选择是用非const 成员函数来表达const 成员函数,反之亦然。但我记得这涉及到一些难看的选角。或者有些丑陋,不确定(抱歉,我现在的互联网连接非常有限)。

#include <string>

//--------------------------------------- Machinery:

template< class Guide, class Result >
struct With_const_like_t_
{
    using T = Result;
};

template< class Guide, class Result >
struct With_const_like_t_<Guide const, Result>
{
    using T = Result const;
};

template< class Guide, class Result >
using With_const_like_ = typename With_const_like_t_<Guide, Result>::T;


//--------------------------------------- Example usage:

class Bork
{
private:
    std::string s_  = "42";

    template< class This_class >
    static auto foo_impl( This_class& o )
        -> With_const_like_<This_class, std::string>&
    { return o.s_; }

public:
    auto foo()
        -> decltype( foo_impl( *this ) )
    { return foo_impl( *this ); }

    auto foo() const
        -> decltype( foo_impl( *this ) )
    { return foo_impl( *this ); }
};

#include <iostream>
#include <typeinfo>
using namespace std;

auto main()
    -> int
{
    Bork v;
    Bork const c;
    v.foo() = "Hi there!";
    #ifdef TEST
        c.foo() = "This assignment to `const` won't compile.";
    #endif
    cout << v.foo() << endl;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-21
    相关资源
    最近更新 更多