Yes, it is probably a weird question, but I tried a lot, and I started to think that maybe is impossible to overload this template function properly:

#include <iterator>

class Foo
{
private:
    const int arr[5] = {10, 20, 30, 40, 50};
public:
    const int* begin() const { return arr; }

friend auto std::begin<>(const Foo &f) -> decltype(f.begin());
}

It always throw the same error (in GCC 12.2.0):

main.cxx:10:13: error: template-id ‘begin<>’ forconst int* std::begin<>(const Foo&)’ does not match any template declaration

I just wanna know if is possible do things like this. Thanks.

  • Apparently it is impossible for this kind of functions to be defined as friends of classes:

    template <typename T>
    auto do_something(T &t) -> decltype(t.private_msg);
    
    class Foo
    {
    private:
        const char *private_msg = "You can't touch me!";
        
    friend auto do_something<>(Foo &f) -> decltype(f.private_msg); // Error!
    };
    
    template <>
    auto do_something<Foo>(Foo &f) -> decltype(f.private_msg) // Error!
    {
        return f.private_msg; // Error!
    }
    

    After trying different combinations, it seems that I managed to get it working with the condition the whole template are considered friends of the class. I don’t know if I should consider it a language problem, but it seems that way, since the template restrictions (in this case) are minor.

    template <typename T>
    auto do_something(T &t) -> decltype(t.private_msg);
    
    class Foo
    {
    private:
        const char *private_msg = "You can't touch me!";
    
    template <typename T>
    friend auto do_something(T &t) -> decltype(t.private_msg); // This works fine!
    };
    
    template <>
    auto do_something<Foo>(Foo &f) -> decltype(f.private_msg)
    {
        return f.private_msg;
    }
    

    Do you think I found an error in the language?

    • ctr1@fl0w.cc
      link
      fedilink
      English
      arrow-up
      2
      ·
      edit-2
      3 months ago

      Ah, nice idea. I’ve tried a few different ways of doing this, and I think what you’re seeing is a discrepancy in how the compiler handles member access into incomplete types. It seems that, in your examples, the compiler is allowing -> decltype(f.private_msg) within the class, but I think it’s not selecting do_something outside of it because it uses decltype(t.private_msg). In my case, I’m not even able to do that within the class.

      For example, since I’m not able to use decltype(f.private_msg) inside the class, I’m using decltype(private_msg) instead, which causes an error at the do_something declaration related to incomplete type (presumably because of the t.private_msg usage):

      // candidate template ignored; member access into incomplete type
      templateclass Tauto do_something(T &t) -> decltype(t.private_msg);
      class Foo {
              const char *private_msg = "You can't touch me!";
              friend auto do_something〈〉(Foo &f) -> decltype(private_msg);
      };
      template 〈〉 auto do_something(Foo &f) -> decltype(f.private_msg) {
              return f.private_msg;
      }
      

      My reasoning is that removing the t.private_msg from the declaration works:

      templateclass Ret, class Tauto do_something(T &t) -> Ret;
      class Foo {
              const char *private_msg = "You can't touch me!";
              friend auto do_something〈〉(Foo &f) -> decltype(private_msg);
      };
      template 〈〉 auto do_something(Foo &f) -> decltype(f.private_msg) {
              return f.private_msg;
      }
      static Foo foo{};
      // this works, but Ret cannot be deduced and must be specified somehow:
      static auto something = do_something〈const char*〉(foo);
      

      The reason your second example works is because the friend template inside the class acts as a template declaration rather than a specialization, which isn’t specialized until after Foo is complete:

      // the do_something inside Foo is a declaration, meaning this isn't used
      // template 〈class T〉
      // auto do_something(T &t) -> decltype(t.private_msg);
      class Foo {
              const char *private_msg = "You can't touch me!";
              templateclass T// t.private_msg is allowed because T is not Foo yet
              friend auto do_something(T &t) -> decltype(t.private_msg);
      };
      template 〈〉 auto do_something(Foo &f) -> decltype(f.private_msg) {
              return f.private_msg;
      }