admin管理员组

文章数量:1389960

I have a template friend function named print. Is there a way to overload print for specific types of contexts (the first parameter)

  1. such that the overload is also a friend and
  2. without forward declaring these contexts in clause.h?

Here are definitions for the class and friend function:

#include <iostream>

struct clause;

template <typename Context>
void print(const Context &, const clause &c);

struct clause { 
private:
  template <typename Context>
  friend void print(const Context &, const clause &c);

  int i = 8;
};

template <typename Context>
void print(const Context &, const clause &c) { 
  std::cerr << c.i << " friend\n";
}

I would like to do something like this:

#include "clause.h"

class special_context {};

// Fails as this is an overload, not a specialization.
void print(const special_context &, const clause &c) {
  std::cerr << c.i << " overload\n"; // not a friend
}

I have a template friend function named print. Is there a way to overload print for specific types of contexts (the first parameter)

  1. such that the overload is also a friend and
  2. without forward declaring these contexts in clause.h?

Here are definitions for the class and friend function:

#include <iostream>

struct clause;

template <typename Context>
void print(const Context &, const clause &c);

struct clause { 
private:
  template <typename Context>
  friend void print(const Context &, const clause &c);

  int i = 8;
};

template <typename Context>
void print(const Context &, const clause &c) { 
  std::cerr << c.i << " friend\n";
}

I would like to do something like this:

#include "clause.h"

class special_context {};

// Fails as this is an overload, not a specialization.
void print(const special_context &, const clause &c) {
  std::cerr << c.i << " overload\n"; // not a friend
}

Share Improve this question edited Mar 15 at 19:14 Rumburak asked Mar 15 at 18:27 RumburakRumburak 3,59318 silver badges28 bronze badges 7
  • And you can't have the Contexts all derive from a common base? – Tim Roberts Commented Mar 15 at 18:39
  • Let's say I could, how would that help (other than with virtual functions)? – Rumburak Commented Mar 15 at 18:42
  • Do you really need templates for this question? You seem to recognize that the functions are overloads, not partial specializations, which is a key factor. You could have a (non-template) struct clause and a pair of (non-template) function overloads, void print(const generic_context &, const clause &) and void print(const special_context &, const clause &). Is there a way to declare both overloads as friend without forward declaring special_context? – JaMiT Commented Mar 15 at 18:42
  • @JaMiT Yes, I need clause to be a template. I know that the parameter is not used here, but it normally would be, e.g. used for the private data member. Updated code with a respective comment. – Rumburak Commented Mar 15 at 18:47
  • 1 @Rumburak "I need clause to be a template." -- You need clause to be a template, but does your question need clause to be a template? If you got an answer where clause is not a template, would you not be able to adapt that answer to your real code? If you can simplify your question by thinking more abstractly, there is a better chance that the question will be applicable to more people in the future. – JaMiT Commented Mar 15 at 18:54
 |  Show 2 more comments

2 Answers 2

Reset to default 4

such that the overload is also a friend and

We can't do that. We have to list overloaded functions in friends.

This can be achieved with functional objects (functors).

#include <functional>
#include <iostream>
#include <type_traits>

template <typename T>
struct clause {
 private:
  friend struct print;

  int i = 8;
};

class special_context {};

struct print {
  template <typename Context, typename T>
  void operator()(const Context&, const clause<T>& c) {
    std::cerr << c.i << " friend\n";
  }

  template <typename T>
  void operator()(const special_context&, const clause<T>& c) {
    std::cerr << c.i << " overload\n";
  }
};

int main() {
  clause<int> c;
  std::invoke(print{}, nullptr, c);  // or print{}(nullptr, c), or print()(nullptr, c);
  std::invoke(print{}, special_context{}, c);
}

https://godbolt./z/4bqvrnar1

Further development - enclose the functional object calls into functions

#include <functional>
#include <iostream>
#include <type_traits>

template <typename T>
struct clause {
 private:
  friend struct printer;

  int i = 8;
};

class special_context {};

struct printer {
  template <typename Context, typename T>
  void operator()(const Context&, const clause<T>& c) {
    std::cerr << c.i << " friend\n";
  }

  template <typename T>
  void operator()(const special_context&, const clause<T>& c) {
    std::cerr << c.i << " overload\n";
  }
};

template <typename Context, typename T>
void print(const Context&, const clause<T>& c) {
  std::invoke(printer{}, nullptr, c);
}

template <typename T>
void print(const special_context& ctx, const clause<T>& c) {
  std::invoke(printer{}, ctx, c);
}

int main() {
  clause<int> c;
  print(nullptr, c);
  print(special_context{}, c);
}

https://godbolt./z/Tb73n1nan

Expanding on 3CxEZiVlQ's answer, using a template class printer does the trick neatly:

This is clause with the default print function, which is implemented in the friend class printer:

// clause.h
#include <iostream>

struct clause {
 private:
  template <typename Context>
  friend struct printer;

  int i = 8;
};

template <typename Context>
struct printer {
  void print(const Context&, const clause& c) {
    std::cerr << c.i << " friend\n";
  }
};

template <typename Context>
void print(const Context& context, const clause& clause) {
  printer<Context>{}.print(context, clause);
}

And then we can specialize the printer:

#include "clause.h"

class special_context {};

template <>
struct printer<special_context> {
  void print(const special_context&, const clause& c) {
    std::cerr << c.i << " overload\n";
  }
};

int main() {
  clause c;
  print(nullptr, c);
  print(special_context{}, c);
}

See https://godbolt./z/zbr3qTn6a

本文标签: cOverloading template friend functionStack Overflow