如何删除类似 const 和非常量成员函数之间的代码重复?

发布于 2024-07-05 19:16:46 字数 945 浏览 13 评论 0 原文

假设我有以下 class X,我想在其中返回对内部成员的访问权限:

class Z
{
    // details
};

class X
{
    std::vector<Z> vecZ;

public:
    Z& Z(size_t index)
    {
        // massive amounts of code for validating index

        Z& ret = vecZ[index];

        // even more code for determining that the Z instance
        // at index is *exactly* the right sort of Z (a process
        // which involves calculating leap years in which
        // religious holidays fall on Tuesdays for
        // the next thousand years or so)

        return ret;
    }
    const Z& Z(size_t index) const
    {
        // identical to non-const X::Z(), except printed in
        // a lighter shade of gray since
        // we're running low on toner by this point
    }
};

两个成员函数 X::Z()X:: Z() const 大括号内有相同的代码。 这是重复的代码并且可能会导致具有复杂逻辑的长函数的维护问题

有没有办法避免这种代码重复?

Let's say I have the following class X where I want to return access to an internal member:

class Z
{
    // details
};

class X
{
    std::vector<Z> vecZ;

public:
    Z& Z(size_t index)
    {
        // massive amounts of code for validating index

        Z& ret = vecZ[index];

        // even more code for determining that the Z instance
        // at index is *exactly* the right sort of Z (a process
        // which involves calculating leap years in which
        // religious holidays fall on Tuesdays for
        // the next thousand years or so)

        return ret;
    }
    const Z& Z(size_t index) const
    {
        // identical to non-const X::Z(), except printed in
        // a lighter shade of gray since
        // we're running low on toner by this point
    }
};

The two member functions X::Z() and X::Z() const have identical code inside the braces. This is duplicate code and can cause maintenance problems for long functions with complex logic.

Is there a way to avoid this code duplication?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(22

谁许谁一生繁华 2024-07-12 19:16:47

我想出了一个可以自动生成常量/非常量函数对的宏。

class A
{
    int x;    
  public:
    MAYBE_CONST(
        CV int &GetX() CV {return x;}
        CV int &GetY() CV {return y;}
    )

    //   Equivalent to:
    // int &GetX() {return x;}
    // int &GetY() {return y;}
    // const int &GetX() const {return x;}
    // const int &GetY() const {return y;}
};

具体实现请见答案末尾。

MAYBE_CONST 的参数重复。 在第一个副本中,CV 被替换为任何内容; 在第二个副本中,它被替换为 const。

CV 在宏参数中出现的次数没有限制。

不过还是有一点不便。 如果 CV 出现在括号内,则这对括号必须以 CV_IN 为前缀:

// Doesn't work
MAYBE_CONST( CV int &foo(CV int &); )

// Works, expands to
//         int &foo(      int &);
//   const int &foo(const int &);
MAYBE_CONST( CV int &foo CV_IN(CV int &); )

实现:

#define MAYBE_CONST(...) IMPL_CV_maybe_const( (IMPL_CV_null,__VA_ARGS__)() )
#define CV )(IMPL_CV_identity,
#define CV_IN(...) )(IMPL_CV_p_open,)(IMPL_CV_null,__VA_ARGS__)(IMPL_CV_p_close,)(IMPL_CV_null,

#define IMPL_CV_null(...)
#define IMPL_CV_identity(...) __VA_ARGS__
#define IMPL_CV_p_open(...) (
#define IMPL_CV_p_close(...) )

#define IMPL_CV_maybe_const(seq) IMPL_CV_a seq IMPL_CV_const_a seq

#define IMPL_CV_body(cv, m, ...) m(cv) __VA_ARGS__

#define IMPL_CV_a(...) __VA_OPT__(IMPL_CV_body(,__VA_ARGS__) IMPL_CV_b)
#define IMPL_CV_b(...) __VA_OPT__(IMPL_CV_body(,__VA_ARGS__) IMPL_CV_a)

#define IMPL_CV_const_a(...) __VA_OPT__(IMPL_CV_body(const,__VA_ARGS__) IMPL_CV_const_b)
#define IMPL_CV_const_b(...) __VA_OPT__(IMPL_CV_body(const,__VA_ARGS__) IMPL_CV_const_a)

不支持 CV_IN< 的 Pre-C++20 实现/代码>:

#define MAYBE_CONST(...) IMPL_MC( ((__VA_ARGS__)) )
#define CV ))((

#define IMPL_MC(seq) \
    IMPL_MC_end(IMPL_MC_a seq) \
    IMPL_MC_end(IMPL_MC_const_0 seq)

#define IMPL_MC_identity(...) __VA_ARGS__
#define IMPL_MC_end(...) IMPL_MC_end_(__VA_ARGS__)
#define IMPL_MC_end_(...) __VA_ARGS__##_end

#define IMPL_MC_a(elem) IMPL_MC_identity elem IMPL_MC_b
#define IMPL_MC_b(elem) IMPL_MC_identity elem IMPL_MC_a
#define IMPL_MC_a_end
#define IMPL_MC_b_end

#define IMPL_MC_const_0(elem)       IMPL_MC_identity elem IMPL_MC_const_a
#define IMPL_MC_const_a(elem) const IMPL_MC_identity elem IMPL_MC_const_b
#define IMPL_MC_const_b(elem) const IMPL_MC_identity elem IMPL_MC_const_a
#define IMPL_MC_const_a_end
#define IMPL_MC_const_b_end

I came up with a macro that generates pairs of const/non-const functions automatically.

class A
{
    int x;    
  public:
    MAYBE_CONST(
        CV int &GetX() CV {return x;}
        CV int &GetY() CV {return y;}
    )

    //   Equivalent to:
    // int &GetX() {return x;}
    // int &GetY() {return y;}
    // const int &GetX() const {return x;}
    // const int &GetY() const {return y;}
};

See the end of the answer for the implementation.

The argument of MAYBE_CONST is duplicated. In the first copy, CV is replaced with nothing; and in the second copy it's replaced with const.

There's no limit on how many times CV can appear in the macro argument.

There's a slight inconvenience though. If CV appears inside of parentheses, this pair of parentheses must be prefixed with CV_IN:

// Doesn't work
MAYBE_CONST( CV int &foo(CV int &); )

// Works, expands to
//         int &foo(      int &);
//   const int &foo(const int &);
MAYBE_CONST( CV int &foo CV_IN(CV int &); )

Implementation:

#define MAYBE_CONST(...) IMPL_CV_maybe_const( (IMPL_CV_null,__VA_ARGS__)() )
#define CV )(IMPL_CV_identity,
#define CV_IN(...) )(IMPL_CV_p_open,)(IMPL_CV_null,__VA_ARGS__)(IMPL_CV_p_close,)(IMPL_CV_null,

#define IMPL_CV_null(...)
#define IMPL_CV_identity(...) __VA_ARGS__
#define IMPL_CV_p_open(...) (
#define IMPL_CV_p_close(...) )

#define IMPL_CV_maybe_const(seq) IMPL_CV_a seq IMPL_CV_const_a seq

#define IMPL_CV_body(cv, m, ...) m(cv) __VA_ARGS__

#define IMPL_CV_a(...) __VA_OPT__(IMPL_CV_body(,__VA_ARGS__) IMPL_CV_b)
#define IMPL_CV_b(...) __VA_OPT__(IMPL_CV_body(,__VA_ARGS__) IMPL_CV_a)

#define IMPL_CV_const_a(...) __VA_OPT__(IMPL_CV_body(const,__VA_ARGS__) IMPL_CV_const_b)
#define IMPL_CV_const_b(...) __VA_OPT__(IMPL_CV_body(const,__VA_ARGS__) IMPL_CV_const_a)

Pre-C++20 implementation that doesn't support CV_IN:

#define MAYBE_CONST(...) IMPL_MC( ((__VA_ARGS__)) )
#define CV ))((

#define IMPL_MC(seq) \
    IMPL_MC_end(IMPL_MC_a seq) \
    IMPL_MC_end(IMPL_MC_const_0 seq)

#define IMPL_MC_identity(...) __VA_ARGS__
#define IMPL_MC_end(...) IMPL_MC_end_(__VA_ARGS__)
#define IMPL_MC_end_(...) __VA_ARGS__##_end

#define IMPL_MC_a(elem) IMPL_MC_identity elem IMPL_MC_b
#define IMPL_MC_b(elem) IMPL_MC_identity elem IMPL_MC_a
#define IMPL_MC_a_end
#define IMPL_MC_b_end

#define IMPL_MC_const_0(elem)       IMPL_MC_identity elem IMPL_MC_const_a
#define IMPL_MC_const_a(elem) const IMPL_MC_identity elem IMPL_MC_const_b
#define IMPL_MC_const_b(elem) const IMPL_MC_identity elem IMPL_MC_const_a
#define IMPL_MC_const_a_end
#define IMPL_MC_const_b_end
美人迟暮 2024-07-12 19:16:47

我为一位朋友做了这件事,他正确地证明了使用 const_cast 的合理性...如果不知道这一点,我可能会做这样的事情(不是很优雅):

#include <iostream>

class MyClass
{

public:

    int getI()
    {
        std::cout << "non-const getter" << std::endl;
        return privateGetI<MyClass, int>(*this);
    }

    const int getI() const
    {
        std::cout << "const getter" << std::endl;
        return privateGetI<const MyClass, const int>(*this);
    }

private:

    template <class C, typename T>
    static T privateGetI(C c)
    {
        //do my stuff
        return c._i;
    }

    int _i;
};

int main()
{
    const MyClass myConstClass = MyClass();
    myConstClass.getI();

    MyClass myNonConstClass;
    myNonConstClass.getI();

    return 0;
}

I did this for a friend who rightfully justified the use of const_cast... not knowing about it I probably would have done something like this (not really elegant) :

#include <iostream>

class MyClass
{

public:

    int getI()
    {
        std::cout << "non-const getter" << std::endl;
        return privateGetI<MyClass, int>(*this);
    }

    const int getI() const
    {
        std::cout << "const getter" << std::endl;
        return privateGetI<const MyClass, const int>(*this);
    }

private:

    template <class C, typename T>
    static T privateGetI(C c)
    {
        //do my stuff
        return c._i;
    }

    int _i;
};

int main()
{
    const MyClass myConstClass = MyClass();
    myConstClass.getI();

    MyClass myNonConstClass;
    myNonConstClass.getI();

    return 0;
}
£噩梦荏苒 2024-07-12 19:16:47

通常,需要 const 和非 const 版本的成员函数是 getter 和 setter。 大多数时候它们都是单行代码,因此代码重复不是问题。

Typically, the member functions for which you need const and non-const versions are getters and setters. Most of the time they are one-liners so code duplication is not an issue.

恬淡成诗 2024-07-12 19:16:47

添加到jwfearn和kevin提供的解决方案中,下面是函数返回shared_ptr时对应的解决方案:

struct C {
  shared_ptr<const char> get() const {
    return c;
  }
  shared_ptr<char> get() {
    return const_pointer_cast<char>(static_cast<const C &>(*this).get());
  }
  shared_ptr<char> c;
};

To add to the solution jwfearn and kevin provided, here's the corresponding solution when the function returns shared_ptr:

struct C {
  shared_ptr<const char> get() const {
    return c;
  }
  shared_ptr<char> get() {
    return const_pointer_cast<char>(static_cast<const C &>(*this).get());
  }
  shared_ptr<char> c;
};
浅浅淡淡 2024-07-12 19:16:47

没有找到我要找的东西,所以我自己推出了几个...

这个有点罗嗦,但具有一次性处理许多同名(和返回类型)的重载方法的优点:

struct C {
  int x[10];

  int const* getp() const { return x; }
  int const* getp(int i) const { return &x[i]; }
  int const* getp(int* p) const { return &x[*p]; }

  int const& getr() const { return x[0]; }
  int const& getr(int i) const { return x[i]; }
  int const& getr(int* p) const { return x[*p]; }

  template<typename... Ts>
  auto* getp(Ts... args) {
    auto const* p = this;
    return const_cast<int*>(p->getp(args...));
  }

  template<typename... Ts>
  auto& getr(Ts... args) {
    auto const* p = this;
    return const_cast<int&>(p->getr(args...));
  }
};

如果每个名称只有一个 const 方法,但仍然有很多方法需要重复,那么您可能更喜欢这样:

  template<typename T, typename... Ts>
  auto* pwrap(T const* (C::*f)(Ts...) const, Ts... args) {
    return const_cast<T*>((this->*f)(args...));
  }

  int* getp_i(int i) { return pwrap(&C::getp_i, i); }
  int* getp_p(int* p) { return pwrap(&C::getp_p, p); }

不幸的是,一旦您开始重载名称(函数指针参数的参数列表),这种情况就会崩溃此时似乎尚未解决,因此无法找到函数参数的匹配项)。 尽管您也可以通过模板来解决这个问题:

  template<typename... Ts>
  auto* getp(Ts... args) { return pwrap<int, Ts...>(&C::getp, args...); }

但是 const 方法的引用参数无法与模板的明显按值参数匹配,并且它会中断。 不知道为什么。原因如下

Didn't find what I was looking for, so I rolled a couple of my own...

This one is a little wordy, but has the advantage of handling many overloaded methods of the same name (and return type) all at once:

struct C {
  int x[10];

  int const* getp() const { return x; }
  int const* getp(int i) const { return &x[i]; }
  int const* getp(int* p) const { return &x[*p]; }

  int const& getr() const { return x[0]; }
  int const& getr(int i) const { return x[i]; }
  int const& getr(int* p) const { return x[*p]; }

  template<typename... Ts>
  auto* getp(Ts... args) {
    auto const* p = this;
    return const_cast<int*>(p->getp(args...));
  }

  template<typename... Ts>
  auto& getr(Ts... args) {
    auto const* p = this;
    return const_cast<int&>(p->getr(args...));
  }
};

If you have only one const method per name, but still plenty of methods to duplicate, then you might prefer this:

  template<typename T, typename... Ts>
  auto* pwrap(T const* (C::*f)(Ts...) const, Ts... args) {
    return const_cast<T*>((this->*f)(args...));
  }

  int* getp_i(int i) { return pwrap(&C::getp_i, i); }
  int* getp_p(int* p) { return pwrap(&C::getp_p, p); }

Unfortunately this breaks down as soon as you start overloading the name (the function pointer argument's argument list seems to be unresolved at that point, so it can't find a match for the function argument). Although you can template your way out of that, too:

  template<typename... Ts>
  auto* getp(Ts... args) { return pwrap<int, Ts...>(&C::getp, args...); }

But reference arguments to the const method fail to match against the apparently by-value arguments to the template and it breaks. Not sure why.Here's why.

糖粟与秋泊 2024-07-12 19:16:47

如何将逻辑转移到私有方法中,并且只在 getters 中执行“获取引用并返回”的操作? 实际上,我会对简单 getter 函数内的 static 和 const 强制转换感到相当困惑,并且我认为除了极少数情况外,这很丑陋!

How about moving the logic into a private method, and only doing the "get the reference and return" stuff inside the getters? Actually, I would be fairly confused about the static and const casts inside a simple getter function, and I'd consider that ugly except for extremely rare circumstances!

请叫√我孤独 2024-07-12 19:16:47

我建议使用私有辅助静态函数模板,如下所示:

class X
{
    std::vector<Z> vecZ;

    // ReturnType is explicitly 'Z&' or 'const Z&'
    // ThisType is deduced to be 'X' or 'const X'
    template <typename ReturnType, typename ThisType>
    static ReturnType Z_impl(ThisType& self, size_t index)
    {
        // massive amounts of code for validating index
        ReturnType ret = self.vecZ[index];
        // even more code for determining, blah, blah...
        return ret;
    }

public:
    Z& Z(size_t index)
    {
        return Z_impl<Z&>(*this, index);
    }
    const Z& Z(size_t index) const
    {
        return Z_impl<const Z&>(*this, index);
    }
};

I'd suggest a private helper static function template, like this:

class X
{
    std::vector<Z> vecZ;

    // ReturnType is explicitly 'Z&' or 'const Z&'
    // ThisType is deduced to be 'X' or 'const X'
    template <typename ReturnType, typename ThisType>
    static ReturnType Z_impl(ThisType& self, size_t index)
    {
        // massive amounts of code for validating index
        ReturnType ret = self.vecZ[index];
        // even more code for determining, blah, blah...
        return ret;
    }

public:
    Z& Z(size_t index)
    {
        return Z_impl<Z&>(*this, index);
    }
    const Z& Z(size_t index) const
    {
        return Z_impl<const Z&>(*this, index);
    }
};
耳根太软 2024-07-12 19:16:47

使用预处理器是作弊吗?

struct A {

    #define GETTER_CORE_CODE       \
    /* line 1 of getter code */    \
    /* line 2 of getter code */    \
    /* .....etc............. */    \
    /* line n of getter code */       

    // ^ NOTE: line continuation char '\' on all lines but the last

   B& get() {
        GETTER_CORE_CODE
   }

   const B& get() const {
        GETTER_CORE_CODE
   }

   #undef GETTER_CORE_CODE

};

它不像模板或强制转换那么花哨,但它确实使您的意图(“这两个函数是相同的”)非常明确。

Is it cheating to use the preprocessor?

struct A {

    #define GETTER_CORE_CODE       \
    /* line 1 of getter code */    \
    /* line 2 of getter code */    \
    /* .....etc............. */    \
    /* line n of getter code */       

    // ^ NOTE: line continuation char '\' on all lines but the last

   B& get() {
        GETTER_CORE_CODE
   }

   const B& get() const {
        GETTER_CORE_CODE
   }

   #undef GETTER_CORE_CODE

};

It's not as fancy as templates or casts, but it does make your intent ("these two functions are to be identical") pretty explicit.

作业与我同在 2024-07-12 19:16:47

令我惊讶的是,有这么多不同的答案,但几乎所有答案都依赖于沉重的模板魔法。 模板很强大,但有时宏在简洁性方面胜过它们。 最大的多功能性通常是通过将两者结合起来实现的。

我编写了一个宏FROM_CONST_OVERLOAD(),可以将其放置在非常量函数中以调用const函数。

用法示例:

class MyClass
{
private:
    std::vector<std::string> data = {"str", "x"};

public:
    // Works for references
    const std::string& GetRef(std::size_t index) const
    {
        return data[index];
    }

    std::string& GetRef(std::size_t index)
    {
        return FROM_CONST_OVERLOAD( GetRef(index) );
    }


    // Works for pointers
    const std::string* GetPtr(std::size_t index) const
    {
        return &data[index];
    }

    std::string* GetPtr(std::size_t index)
    {
        return FROM_CONST_OVERLOAD( GetPtr(index) );
    }
};

简单且可重用的实现:

template <typename T>
T& WithoutConst(const T& ref)
{
    return const_cast<T&>(ref);
}

template <typename T>
T* WithoutConst(const T* ptr)
{
    return const_cast<T*>(ptr);
}

template <typename T>
const T* WithConst(T* ptr)
{
    return ptr;
}

#define FROM_CONST_OVERLOAD(FunctionCall) \
  WithoutConst(WithConst(this)->FunctionCall)

说明:

正如许多答案中所发布的那样,避免非常量成员函数中的代码重复的典型模式是这样的:

return const_cast<Result&>( static_cast<const MyClass*>(this)->Method(args) );

使用类型推断可以避免很多此类样板文件。 首先,const_cast 可以封装在 WithoutConst() 中,它会推断其参数的类型并删除 const 限定符。 其次,可以在 WithConst() 中使用类似的方法来对 this 指针进行 const 限定,从而可以调用 const 重载方法。

其余部分是一个简单的宏,它在调用前加上正确限定的 this-> 前缀,并从结果中删除 const。 由于宏中使用的表达式几乎总是带有 1:1 转发参数的简单函数调用,因此不会出现宏的缺点(例如多重求值)。也可以使用省略号和 __VA_ARGS__,但是不需要,因为逗号(作为参数分隔符)出现在括号内。

这种方法有几个好处:

  • 语法最少且自然 - 只需将调用包装在 FROM_CONST_OVERLOAD( )
  • 不需要额外的成员函数
  • 与 C++98 兼容
  • 简单实现,无模板元编程和零依赖性
  • 可扩展:其他可以添加 const 关系(如 const_iterator、std::shared_ptr 等)。 为此,只需为相应类型重载 WithoutConst() 即可。

限制:此解决方案针对非常量重载与常量重载完全相同的场景进行了优化,因此参数可以按 1:1 转发。 如果您的逻辑不同并且您没有通过 this->Method(args) 调用 const 版本,您可以考虑其他方法。

It's surprising to me that there are so many different answers, yet almost all rely on heavy template magic. Templates are powerful, but sometimes macros beat them in conciseness. Maximum versatility is often achieved by combining both.

I wrote a macro FROM_CONST_OVERLOAD() which can be placed in the non-const function to invoke the const function.

Example usage:

class MyClass
{
private:
    std::vector<std::string> data = {"str", "x"};

public:
    // Works for references
    const std::string& GetRef(std::size_t index) const
    {
        return data[index];
    }

    std::string& GetRef(std::size_t index)
    {
        return FROM_CONST_OVERLOAD( GetRef(index) );
    }


    // Works for pointers
    const std::string* GetPtr(std::size_t index) const
    {
        return &data[index];
    }

    std::string* GetPtr(std::size_t index)
    {
        return FROM_CONST_OVERLOAD( GetPtr(index) );
    }
};

Simple and reusable implementation:

template <typename T>
T& WithoutConst(const T& ref)
{
    return const_cast<T&>(ref);
}

template <typename T>
T* WithoutConst(const T* ptr)
{
    return const_cast<T*>(ptr);
}

template <typename T>
const T* WithConst(T* ptr)
{
    return ptr;
}

#define FROM_CONST_OVERLOAD(FunctionCall) \
  WithoutConst(WithConst(this)->FunctionCall)

Explanation:

As posted in many answers, the typical pattern to avoid code duplication in a non-const member function is this:

return const_cast<Result&>( static_cast<const MyClass*>(this)->Method(args) );

A lot of this boilerplate can be avoided using type inference. First, const_cast can be encapsulated in WithoutConst(), which infers the type of its argument and removes the const-qualifier. Second, a similar approach can be used in WithConst() to const-qualify the this pointer, which enables calling the const-overloaded method.

The rest is a simple macro that prefixes the call with the correctly qualified this-> and removes const from the result. Since the expression used in the macro is almost always a simple function call with 1:1 forwarded arguments, drawbacks of macros such as multiple evaluation do not kick in. The ellipsis and __VA_ARGS__ could also be used, but should not be needed because commas (as argument separators) occur within parentheses.

This approach has several benefits:

  • Minimal and natural syntax -- just wrap the call in FROM_CONST_OVERLOAD( )
  • No extra member function required
  • Compatible with C++98
  • Simple implementation, no template metaprogramming and zero dependencies
  • Extensible: other const relations can be added (like const_iterator, std::shared_ptr<const T>, etc.). For this, simply overload WithoutConst() for the corresponding types.

Limitations: this solution is optimized for scenarios where the non-const overload is doing exactly the same as the const overload, so that arguments can be forwarded 1:1. If your logic differs and you are not calling the const version via this->Method(args), you may consider other approaches.

西瑶 2024-07-12 19:16:47

我做了一个宏
https://gist.github.com/esnosy/947565a4e651991dc6a25f3e9f467285

#include <utility>

// Based on https://en.cppreference.com/mwiki/index.php?title=cpp/utility/as_const&oldid=151976#Possible_implementation
template <typename T> constexpr T &as_non_const(const T &t) noexcept { return const_cast<T &>(t); }

#define NON_CONST_GETTER(const_getter)                                                                                 \
  auto &const_getter() { return as_non_const(std::as_const(*this).const_getter()); }

// Example
class C {
private:
  int secret = 42;
public:
  const int &get() const { return secret; }
  NON_CONST_GETTER(get);
};

I made a macro
https://gist.github.com/esnosy/947565a4e651991dc6a25f3e9f467285

#include <utility>

// Based on https://en.cppreference.com/mwiki/index.php?title=cpp/utility/as_const&oldid=151976#Possible_implementation
template <typename T> constexpr T &as_non_const(const T &t) noexcept { return const_cast<T &>(t); }

#define NON_CONST_GETTER(const_getter)                                                                                 \
  auto &const_getter() { return as_non_const(std::as_const(*this).const_getter()); }

// Example
class C {
private:
  int secret = 42;
public:
  const int &get() const { return secret; }
  NON_CONST_GETTER(get);
};
灵芸 2024-07-12 19:16:47

这篇 DDJ 文章展示了一种使用模板专业化的方法,不需要使用 const_cast。 对于这样一个简单的功能,它实际上是不需要的。

boost::any_cast (在某一时刻,它不再是了)使用 const 版本中的 const_cast 调用非 const 版本来避免重复。 不过,您不能将 const 语义强加于非常量版本,因此您必须对此非常小心。

最后,只要两个片段直接重叠,一些代码重复就可以了。

This DDJ article shows a way using template specialization that doesn't require you to use const_cast. For such a simple function it really isn't needed though.

boost::any_cast (at one point, it doesn't any more) uses a const_cast from the const version calling the non-const version to avoid duplication. You can't impose const semantics on the non-const version though so you have to be very careful with that.

In the end some code duplication is okay as long as the two snippets are directly on top of each other.

少年亿悲伤 2024-07-12 19:16:46

有关详细说明,请参阅标题“避免在 const 和非 const 成员函数中出现重复”,第 144 页。 23,在 Effective C++ 中的第 3 项“尽可能使用 const,3d 编辑,作者:Scott Meyers,ISBN-13:9780321334879。

alt text

这是 Meyers 的解决方案(简化):

struct C {
  const char & get() const {
    return c;
  }
  char & get() {
    return const_cast<char &>(static_cast<const C &>(*this).get());
  }
  char c;
};

两个强制转换和函数调用可能很难看,但在非 const 方法中是正确的,因为这意味着该对象不是首先是const。 (迈耶斯对此进行了深入的讨论。)

For a detailed explanation, please see the heading "Avoid Duplication in const and Non-const Member Function," on p. 23, in Item 3 "Use const whenever possible," in Effective C++, 3d ed by Scott Meyers, ISBN-13: 9780321334879.

alt text

Here's Meyers' solution (simplified):

struct C {
  const char & get() const {
    return c;
  }
  char & get() {
    return const_cast<char &>(static_cast<const C &>(*this).get());
  }
  char c;
};

The two casts and function call may be ugly, but it's correct in a non-const method as that implies the object was not const to begin with. (Meyers has a thorough discussion of this.)

往日 2024-07-12 19:16:46

C++17 更新了这个问题的最佳答案:

T const & f() const {
    return something_complicated();
}
T & f() {
    return const_cast<T &>(std::as_const(*this).f());
}

这有以下优点:

  • 发生的事情很明显
  • 代码开销最小——它适合单行
  • 很难出错(只能丢弃 易失性偶然,但<代码>易失性是一个罕见的限定符)

如果你想走完整的推导路线那么可以通过有一个辅助函数来完成

template<typename T>
constexpr T & as_mutable(T const & value) noexcept {
    return const_cast<T &>(value);
}
template<typename T>
constexpr T * as_mutable(T const * value) noexcept {
    return const_cast<T *>(value);
}
template<typename T>
constexpr T * as_mutable(T * value) noexcept {
    return value;
}
template<typename T>
void as_mutable(T const &&) = delete;

现在你甚至不能搞砸易失性,用法如下

decltype(auto) f() const {
    return something_complicated();
}
decltype(auto) f() {
    return as_mutable(std::as_const(*this).f());
}

C++17 has updated the best answer for this question:

T const & f() const {
    return something_complicated();
}
T & f() {
    return const_cast<T &>(std::as_const(*this).f());
}

This has the advantages that it:

  • Is obvious what is going on
  • Has minimal code overhead -- it fits in a single line
  • Is hard to get wrong (can only cast away volatile by accident, but volatile is a rare qualifier)

If you want to go the full deduction route then that can be accomplished by having a helper function

template<typename T>
constexpr T & as_mutable(T const & value) noexcept {
    return const_cast<T &>(value);
}
template<typename T>
constexpr T * as_mutable(T const * value) noexcept {
    return const_cast<T *>(value);
}
template<typename T>
constexpr T * as_mutable(T * value) noexcept {
    return value;
}
template<typename T>
void as_mutable(T const &&) = delete;

Now you can't even mess up volatile, and the usage looks like

decltype(auto) f() const {
    return something_complicated();
}
decltype(auto) f() {
    return as_mutable(std::as_const(*this).f());
}
提笔书几行 2024-07-12 19:16:46

是的,可以避免代码重复。 您需要使用 const 成员函数来具有逻辑,并让非常量成员函数调用 const 成员函数并将返回值重新转换为非常量引用(如果函数返回指针,则为指针):

class X
{
   std::vector<Z> vecZ;

public:
   const Z& z(size_t index) const
   {
      // same really-really-really long access 
      // and checking code as in OP
      // ...
      return vecZ[index];
   }

   Z& z(size_t index)
   {
      // One line. One ugly, ugly line - but just one line!
      return const_cast<Z&>( static_cast<const X&>(*this).z(index) );
   }

 #if 0 // A slightly less-ugly version
   Z& Z(size_t index)
   {
      // Two lines -- one cast. This is slightly less ugly but takes an extra line.
      const X& constMe = *this;
      return const_cast<Z&>( constMe.z(index) );
   }
 #endif
};

< strong>注意:重要的是,您不要将逻辑放入非常量函数中并让 const 函数调用非常量函数 - 这可能会导致未定义行为。 原因是常量类实例被转换为非常量实例。 非常量成员函数可能会意外修改类,C++ 标准规定这将导致未定义的行为。

Yes, it is possible to avoid the code duplication. You need to use the const member function to have the logic and have the non-const member function call the const member function and re-cast the return value to a non-const reference (or pointer if the functions returns a pointer):

class X
{
   std::vector<Z> vecZ;

public:
   const Z& z(size_t index) const
   {
      // same really-really-really long access 
      // and checking code as in OP
      // ...
      return vecZ[index];
   }

   Z& z(size_t index)
   {
      // One line. One ugly, ugly line - but just one line!
      return const_cast<Z&>( static_cast<const X&>(*this).z(index) );
   }

 #if 0 // A slightly less-ugly version
   Z& Z(size_t index)
   {
      // Two lines -- one cast. This is slightly less ugly but takes an extra line.
      const X& constMe = *this;
      return const_cast<Z&>( constMe.z(index) );
   }
 #endif
};

NOTE: It is important that you do NOT put the logic in the non-const function and have the const-function call the non-const function -- it may result in undefined behavior. The reason is that a constant class instance gets cast as a non-constant instance. The non-const member function may accidentally modify the class, which the C++ standard states will result in undefined behavior.

女皇必胜 2024-07-12 19:16:46

由于 显式对象参数

struct s {
    auto && f(this auto && self) {
        // all the common code goes here
    }
};

单个函数模板可作为普通成员函数进行调用,并为您推导出正确的引用类型。 不会出现错误的转换,也不会为概念上是一件事的东西编写多个函数。


注意:此功能是由P0847:推论添加的。

C++23 has updated the best answer for this question thanks to explicit object parameters.

struct s {
    auto && f(this auto && self) {
        // all the common code goes here
    }
};

A single function template is callable as a normal member function and deduces the correct reference type for you. No casting to get wrong, no writing multiple functions for something that is conceptually one thing.


Note: this feature was added by P0847: Deducing this.

孤芳又自赏 2024-07-12 19:16:46

我认为 Scott Meyers 的解决方案可以通过使用模板辅助函数在 C++11 中得到改进。 这使得意图更加明显,并且可以被许多其他 getter 重用。

template <typename T>
struct NonConst {typedef T type;};
template <typename T>
struct NonConst<T const> {typedef T type;}; //by value
template <typename T>
struct NonConst<T const&> {typedef T& type;}; //by reference
template <typename T>
struct NonConst<T const*> {typedef T* type;}; //by pointer
template <typename T>
struct NonConst<T const&&> {typedef T&& type;}; //by rvalue-reference

template<typename TConstReturn, class TObj, typename... TArgs>
typename NonConst<TConstReturn>::type likeConstVersion(
   TObj const* obj,
   TConstReturn (TObj::* memFun)(TArgs...) const,
   TArgs&&... args) {
      return const_cast<typename NonConst<TConstReturn>::type>(
         (obj->*memFun)(std::forward<TArgs>(args)...));
}

该辅助函数可以通过以下方式使用。

struct T {
   int arr[100];

   int const& getElement(size_t i) const{
      return arr[i];
   }

   int& getElement(size_t i) {
      return likeConstVersion(this, &T::getElement, i);
   }
};

第一个参数始终是 this 指针。 第二个是指向要调用的成员函数的指针。 之后可以传递任意数量的附加参数,以便将它们转发给函数。
由于可变参数模板,这需要 C++11。

I think Scott Meyers' solution can be improved in C++11 by using a tempate helper function. This makes the intent much more obvious and can be reused for many other getters.

template <typename T>
struct NonConst {typedef T type;};
template <typename T>
struct NonConst<T const> {typedef T type;}; //by value
template <typename T>
struct NonConst<T const&> {typedef T& type;}; //by reference
template <typename T>
struct NonConst<T const*> {typedef T* type;}; //by pointer
template <typename T>
struct NonConst<T const&&> {typedef T&& type;}; //by rvalue-reference

template<typename TConstReturn, class TObj, typename... TArgs>
typename NonConst<TConstReturn>::type likeConstVersion(
   TObj const* obj,
   TConstReturn (TObj::* memFun)(TArgs...) const,
   TArgs&&... args) {
      return const_cast<typename NonConst<TConstReturn>::type>(
         (obj->*memFun)(std::forward<TArgs>(args)...));
}

This helper function can be used the following way.

struct T {
   int arr[100];

   int const& getElement(size_t i) const{
      return arr[i];
   }

   int& getElement(size_t i) {
      return likeConstVersion(this, &T::getElement, i);
   }
};

The first argument is always the this-pointer. The second is the pointer to the member function to call. After that an arbitrary amount of additional arguments can be passed so that they can be forwarded to the function.
This needs C++11 because of the variadic templates.

你在看孤独的风景 2024-07-12 19:16:46

比 Meyers 更冗长一些,但我可能会这样做:

class X {

    private:

    // This method MUST NOT be called except from boilerplate accessors.
    Z &_getZ(size_t index) const {
        return something;
    }

    // boilerplate accessors
    public:
    Z &getZ(size_t index)             { return _getZ(index); }
    const Z &getZ(size_t index) const { return _getZ(index); }
};

私有方法有一个不受欢迎的属性,即它返回一个非常量 Z& 。 对于 const 实例,这就是它是私有的。 私有方法可能会破坏外部接口的不变量(在这种情况下,所需的不变量是“const 对象不能通过通过它获得的对其拥有的对象的引用来修改”)。

请注意,注释是模式的一部分 - _getZ 的接口指定调用它永远无效(显然,除了访问器之外):无论如何,这样做没有任何好处,因为它需要多输入 1 个字符并且不会导致代码更小或更快。 调用该方法相当于使用 const_cast 调用访问器之一,您也不想这样做。 如果您担心错误会变得明显(这是一个公平的目标),那么请将其命名为 const_cast_getZ 而不是 _getZ。

顺便说一句,我很欣赏迈耶斯的解决方案。 我对它没有哲学上的反对意见。 不过,就我个人而言,我更喜欢一点点受控重复,以及只能在某些严格控制的情况下调用的私有方法,而不是看起来像线路噪声的方法。 选择你的毒药并坚持下去。

[编辑:Kevin 正确地指出 _getZ 可能想要调用另一个方法(例如generateZ),该方法与 getZ 一样是 const 专用的。 在这种情况下,_getZ 将看到一个 const Z& 并且必须在返回之前对其进行 const_cast 。 这仍然是安全的,因为样板访问器会监管一切,但它的安全性并不明显。 此外,如果您这样做,然后将generateZ更改为始终返回const,那么您还需要将getZ更改为始终返回const,但编译器不会告诉您这样做。

关于编译器的后一点也适用于 Meyers 推荐的模式,但关于非显而易见的 const_cast 的第一点则不然。 所以总的来说,我认为如果 _getZ 结果需要一个 const_cast 作为它的返回值,那么这个模式比 Meyers 的模式失去了很多价值。 由于与迈耶斯相比它也有缺点,我想在这种情况下我会改用他的。 从一种重构到另一种很容易——它不会影响类中的任何其他有效代码,因为只有无效代码和样板文件调用 _getZ。]

A bit more verbose than Meyers, but I might do this:

class X {

    private:

    // This method MUST NOT be called except from boilerplate accessors.
    Z &_getZ(size_t index) const {
        return something;
    }

    // boilerplate accessors
    public:
    Z &getZ(size_t index)             { return _getZ(index); }
    const Z &getZ(size_t index) const { return _getZ(index); }
};

The private method has the undesirable property that it returns a non-const Z& for a const instance, which is why it's private. Private methods may break invariants of the external interface (in this case the desired invariant is "a const object cannot be modified via references obtained through it to objects it has-a").

Note that the comments are part of the pattern - _getZ's interface specifies that it is never valid to call it (aside from the accessors, obviously): there's no conceivable benefit to doing so anyway, because it's 1 more character to type and won't result in smaller or faster code. Calling the method is equivalent to calling one of the accessors with a const_cast, and you wouldn't want to do that either. If you're worried about making errors obvious (and that's a fair goal), then call it const_cast_getZ instead of _getZ.

By the way, I appreciate Meyers's solution. I have no philosophical objection to it. Personally, though, I prefer a tiny bit of controlled repetition, and a private method that must only be called in certain tightly-controlled circumstances, over a method that looks like line noise. Pick your poison and stick with it.

[Edit: Kevin has rightly pointed out that _getZ might want to call a further method (say generateZ) which is const-specialised in the same way getZ is. In this case, _getZ would see a const Z& and have to const_cast it before return. That's still safe, since the boilerplate accessor polices everything, but it's not outstandingly obvious that it's safe. Furthermore, if you do that and then later change generateZ to always return const, then you also need to change getZ to always return const, but the compiler won't tell you that you do.

That latter point about the compiler is also true of Meyers's recommended pattern, but the first point about a non-obvious const_cast isn't. So on balance I think that if _getZ turns out to need a const_cast for its return value, then this pattern loses a lot of its value over Meyers's. Since it also suffers disadvantages compared to Meyers's, I think I would switch to his in that situation. Refactoring from one to the other is easy -- it doesn't affect any other valid code in the class, since only invalid code and the boilerplate calls _getZ.]

懵少女 2024-07-12 19:16:46

好问题和好答案。 我有另一个解决方案,不使用强制转换:

class X {

private:

    std::vector<Z> v;

    template<typename InstanceType>
    static auto get(InstanceType& instance, std::size_t i) -> decltype(instance.get(i)) {
        // massive amounts of code for validating index
        // the instance variable has to be used to access class members
        return instance.v[i];
    }

public:

    const Z& get(std::size_t i) const {
        return get(*this, i);
    }

    Z& get(std::size_t i) {
        return get(*this, i);
    }

};

但是,它的丑陋之处在于需要静态成员并且需要在其中使用 instance 变量。

我没有考虑该解决方案的所有可能(负面)影响。 如果有的话请告诉我。

Nice question and nice answers. I have another solution, that uses no casts:

class X {

private:

    std::vector<Z> v;

    template<typename InstanceType>
    static auto get(InstanceType& instance, std::size_t i) -> decltype(instance.get(i)) {
        // massive amounts of code for validating index
        // the instance variable has to be used to access class members
        return instance.v[i];
    }

public:

    const Z& get(std::size_t i) const {
        return get(*this, i);
    }

    Z& get(std::size_t i) {
        return get(*this, i);
    }

};

However, it has the ugliness of requiring a static member and the need of using the instance variable inside it.

I did not consider all the possible (negative) implications of this solution. Please let me know if any.

凶凌 2024-07-12 19:16:46

如果您不喜欢 const 转换,我可以使用 另一个答案,带有可选的 SFINAE 测试。

#include <type_traits>

#define REQUIRES(...)         class = std::enable_if_t<(__VA_ARGS__)>
#define REQUIRES_CV_OF(A,B)   REQUIRES( std::is_same_v< std::remove_cv_t< A >, B > )

class Foobar {
private:
    int something;

    template<class FOOBAR, REQUIRES_CV_OF(FOOBAR, Foobar)>
    static auto& _getSomething(FOOBAR& self, int index) {
        // big, non-trivial chunk of code...
        return self.something;
    }

public:
    auto& getSomething(int index)       { return _getSomething(*this, index); }
    auto& getSomething(int index) const { return _getSomething(*this, index); }
};

完整版:https://godbolt.org/z/mMK4r3

If you don't like const casting, I use this C++17 version of the template static helper function suggested by another answer, with and optional SFINAE test.

#include <type_traits>

#define REQUIRES(...)         class = std::enable_if_t<(__VA_ARGS__)>
#define REQUIRES_CV_OF(A,B)   REQUIRES( std::is_same_v< std::remove_cv_t< A >, B > )

class Foobar {
private:
    int something;

    template<class FOOBAR, REQUIRES_CV_OF(FOOBAR, Foobar)>
    static auto& _getSomething(FOOBAR& self, int index) {
        // big, non-trivial chunk of code...
        return self.something;
    }

public:
    auto& getSomething(int index)       { return _getSomething(*this, index); }
    auto& getSomething(int index) const { return _getSomething(*this, index); }
};

Full version: https://godbolt.org/z/mMK4r3

离去的眼神 2024-07-12 19:16:46

虽然这里的大多数答案建议使用 const_cast ,但 CppCoreGuidelines 有一个 部分 关于这一点:

相反,更喜欢共享实现。 通常,您可以让非常量函数调用 const 函数。 然而,当存在复杂的逻辑时,这可能会导致以下仍然诉诸 const_cast 的模式:

class Foo {
public:
    // not great, non-const calls const version but resorts to const_cast
    Bar& get_bar()
    {
        return const_cast<Bar&>(static_cast<const Foo&>(*this).get_bar());
    }
    const Bar& get_bar() const
    {
        /* the complex logic around getting a const reference to my_bar */
    }
private:
    Bar my_bar;
};

虽然这种模式在正确应用时是安全的,因为
调用者必须首先有一个非常量对象,这并不理想
因为安全性很难作为检查规则自动执行。

相反,更喜欢将公共代码放在公共辅助函数中——
并将其设为模板,以便它推导 const。 这不使用任何
const_cast 根本:

class Foo {
public:                         // good
          Bar& get_bar()       { return get_bar_impl(*this); }
    const Bar& get_bar() const { return get_bar_impl(*this); }
private:
    Bar my_bar;

    template<class T>           // good, deduces whether T is const or non-const
    static auto& get_bar_impl(T& t)
        { /* the complex logic around getting a possibly-const reference to my_bar */ }
};

注意:不要在模板内进行大量的非依赖工作,这会导致代码膨胀。 例如,如果 get_bar_impl 的全部或部分可以是非依赖的并分解为公共非模板函数,则可以进一步改进,从而可能大大减少代码大小。

While most of answers here suggest to use a const_cast, CppCoreGuidelines have a section about that:

Instead, prefer to share implementations. Normally, you can just have the non-const function call the const function. However, when there is complex logic this can lead to the following pattern that still resorts to a const_cast:

class Foo {
public:
    // not great, non-const calls const version but resorts to const_cast
    Bar& get_bar()
    {
        return const_cast<Bar&>(static_cast<const Foo&>(*this).get_bar());
    }
    const Bar& get_bar() const
    {
        /* the complex logic around getting a const reference to my_bar */
    }
private:
    Bar my_bar;
};

Although this pattern is safe when applied correctly, because the
caller must have had a non-const object to begin with, it's not ideal
because the safety is hard to enforce automatically as a checker rule.

Instead, prefer to put the common code in a common helper function --
and make it a template so that it deduces const. This doesn't use any
const_cast at all:

class Foo {
public:                         // good
          Bar& get_bar()       { return get_bar_impl(*this); }
    const Bar& get_bar() const { return get_bar_impl(*this); }
private:
    Bar my_bar;

    template<class T>           // good, deduces whether T is const or non-const
    static auto& get_bar_impl(T& t)
        { /* the complex logic around getting a possibly-const reference to my_bar */ }
};

Note: Don't do large non-dependent work inside a template, which leads to code bloat. For example, a further improvement would be if all or part of get_bar_impl can be non-dependent and factored out into a common non-template function, for a potentially big reduction in code size.

早茶月光 2024-07-12 19:16:46

的人(像我一样)

  • 对于那些使用c++17
  • 想要添加最少量的样板/重复并且
  • 不介意使用 (在等待元类时...),

这是另一种做法:

#include <utility>
#include <type_traits>

template <typename T> struct NonConst;
template <typename T> struct NonConst<T const&> {using type = T&;};
template <typename T> struct NonConst<T const*> {using type = T*;};

#define NON_CONST(func)                                                     \
    template <typename... T> auto func(T&&... a)                            \
        -> typename NonConst<decltype(func(std::forward<T>(a)...))>::type   \
    {                                                                       \
        return const_cast<decltype(func(std::forward<T>(a)...))>(           \
            std::as_const(*this).func(std::forward<T>(a)...));              \
    }

它基本上是 @Pait、@DavidStone 和 @sh1 的答案的混合(编辑:以及 @cdhowie 的改进)。 它添加到表中的内容是,您只需要一行额外的代码,它只是简单地命名函数(但没有参数或返回类型重复):

class X
{
    const Z& get(size_t index) const { ... }
    NON_CONST(get)
};

注意:gcc 无法在 8.1、clang-5 及更高版本之前编译此代码,如下所示MSVC-19 很高兴(根据编译器资源管理器)。

For those (like me) who

  • use c++17
  • want to add the least amount of boilerplate/repetition and
  • don't mind using macros (while waiting for meta-classes...),

here is another take:

#include <utility>
#include <type_traits>

template <typename T> struct NonConst;
template <typename T> struct NonConst<T const&> {using type = T&;};
template <typename T> struct NonConst<T const*> {using type = T*;};

#define NON_CONST(func)                                                     \
    template <typename... T> auto func(T&&... a)                            \
        -> typename NonConst<decltype(func(std::forward<T>(a)...))>::type   \
    {                                                                       \
        return const_cast<decltype(func(std::forward<T>(a)...))>(           \
            std::as_const(*this).func(std::forward<T>(a)...));              \
    }

It is basically a mix of the answers from @Pait, @DavidStone and @sh1 (EDIT: and an improvement from @cdhowie). What it adds to the table is that you get away with only one extra line of code which simply names the function (but no argument or return type duplication):

class X
{
    const Z& get(size_t index) const { ... }
    NON_CONST(get)
};

Note: gcc fails to compile this prior to 8.1, clang-5 and upwards as well as MSVC-19 are happy (according to the compiler explorer).

懒猫 2024-07-12 19:16:46

您也可以使用模板来解决这个问题。 这个解决方案有点丑陋(但丑陋隐藏在 .cpp 文件中),但它确实提供了编译器对常量的检查,并且没有代码重复。

.h 文件:

#include <vector>

class Z
{
    // details
};

class X
{
    std::vector<Z> vecZ;

public:
    const std::vector<Z>& GetVector() const { return vecZ; }
    std::vector<Z>& GetVector() { return vecZ; }

    Z& GetZ( size_t index );
    const Z& GetZ( size_t index ) const;
};

.cpp 文件:

#include "constnonconst.h"

template< class ParentPtr, class Child >
Child& GetZImpl( ParentPtr parent, size_t index )
{
    // ... massive amounts of code ...

    // Note you may only use methods of X here that are
    // available in both const and non-const varieties.

    Child& ret = parent->GetVector()[index];

    // ... even more code ...

    return ret;
}

Z& X::GetZ( size_t index )
{
    return GetZImpl< X*, Z >( this, index );
}

const Z& X::GetZ( size_t index ) const
{
    return GetZImpl< const X*, const Z >( this, index );
}

我看到的主要缺点是,因为该方法的所有复杂实现都在全局函数中,所以您要么需要使用上面的 GetVector() 等公共方法来获取 X 的成员(其中总是需要有 const 和非 const 版本)或者你可以让这个函数成为一个朋友。 但我不喜欢朋友。

[编辑:删除了测试期间添加的不需要的 cstdio 包含内容。]

You could also solve this with templates. This solution is slightly ugly (but the ugliness is hidden in the .cpp file) but it does provide compiler checking of constness, and no code duplication.

.h file:

#include <vector>

class Z
{
    // details
};

class X
{
    std::vector<Z> vecZ;

public:
    const std::vector<Z>& GetVector() const { return vecZ; }
    std::vector<Z>& GetVector() { return vecZ; }

    Z& GetZ( size_t index );
    const Z& GetZ( size_t index ) const;
};

.cpp file:

#include "constnonconst.h"

template< class ParentPtr, class Child >
Child& GetZImpl( ParentPtr parent, size_t index )
{
    // ... massive amounts of code ...

    // Note you may only use methods of X here that are
    // available in both const and non-const varieties.

    Child& ret = parent->GetVector()[index];

    // ... even more code ...

    return ret;
}

Z& X::GetZ( size_t index )
{
    return GetZImpl< X*, Z >( this, index );
}

const Z& X::GetZ( size_t index ) const
{
    return GetZImpl< const X*, const Z >( this, index );
}

The main disadvantage I can see is that because all the complex implementation of the method is in a global function, you either need to get hold of the members of X using public methods like GetVector() above (of which there always need to be a const and non-const version) or you could make this function a friend. But I don't like friends.

[Edit: removed unneeded include of cstdio added during testing.]

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文