最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

C++ friend injection - how does it work, and what are the rules? - Stack Overflow

programmeradmin6浏览0评论

In a Cppcon talk, the term "friend injection" was brought up, and the following code sample was provided:

using size_t = decltype(sizeof(int));

namespace detail {
template<auto> struct info { constexpr auto friend get(info); };
template<class T> struct type {
  using value_type = T;
  static constexpr size_t id{};
  constexpr auto friend get(info<&id>) { return type{}; }
};
template<class T> auto remove_const(const T) -> T;
} // namespace detail

template<class T>
inline constexpr auto meta = &detail::type<T>::id;

using info = decltype(detail::remove_const(meta<void>));

template<auto meta>
using type_of = typename decltype(get(detail::info<meta>{}))::value_type;

int main() {
    type_of<meta<int>> ans = 42;
    return ans;
}

I have a few questions regarding this code:

  1. Inside of the type struct, what is the purpose of id?

  2. What exactly is "friend injection" enabling here?

  3. How is get being invoked here, seemingly in a context outside of both info and type?

In a Cppcon talk, the term "friend injection" was brought up, and the following code sample was provided:

using size_t = decltype(sizeof(int));

namespace detail {
template<auto> struct info { constexpr auto friend get(info); };
template<class T> struct type {
  using value_type = T;
  static constexpr size_t id{};
  constexpr auto friend get(info<&id>) { return type{}; }
};
template<class T> auto remove_const(const T) -> T;
} // namespace detail

template<class T>
inline constexpr auto meta = &detail::type<T>::id;

using info = decltype(detail::remove_const(meta<void>));

template<auto meta>
using type_of = typename decltype(get(detail::info<meta>{}))::value_type;

int main() {
    type_of<meta<int>> ans = 42;
    return ans;
}

I have a few questions regarding this code:

  1. Inside of the type struct, what is the purpose of id?

  2. What exactly is "friend injection" enabling here?

  3. How is get being invoked here, seemingly in a context outside of both info and type?

Share Improve this question edited Mar 19 at 21:35 Remy Lebeau 601k36 gold badges507 silver badges851 bronze badges asked Mar 19 at 16:51 coglecogle 1,1011 gold badge14 silver badges27 bronze badges 6
  • 1 On talk slides I've found this godbolt link. This should be helpful. – Marek R Commented Mar 19 at 17:05
  • 1 Your code doesn't compile. In this snippet id is useless. – HolyBlackCat Commented Mar 19 at 17:14
  • @HolyBlackCat only last line is unique, rest is in the link I've provided from slides of the presentation. Here is repro. – Marek R Commented Mar 19 at 17:27
  • 2 @MarekR Sure, but I'd rather have a minimal reproducible example in the question. – HolyBlackCat Commented Mar 19 at 17:40
  • 1 Short note: By convention, using backticks is for code, but friend injection is not code. ;) – Ulrich Eckhardt Commented Mar 19 at 18:38
 |  Show 1 more comment

1 Answer 1

Reset to default 4

The example feels a bit overcomplicated. Here's a simpler one:

#include <iostream>

template <typename Key>
struct Reader
{
    constexpr auto friend get(Reader<Key>);
};

template <typename Key, auto Value>
struct Writer
{
    constexpr auto friend get(Reader<Key>) {return Value;}
};


struct MyKey1 {};
struct MyKey2 {};

int main()
{
    (void)Writer<MyKey1, 42>{};
    (void)Writer<MyKey2, 43>{};

    std::cout << get(Reader<MyKey1>{}) << '\n'; // 42
    std::cout << get(Reader<MyKey2>{}) << '\n'; // 43
}

The point of friend injection is that it gives you a compile-time map, where keys and values can be arbitrary types or values. I've chosen a single typename key and an auto value here, but it can be anything.

The map is write-only. New elements can be added at any time, but the previous one can't be erased. Unless you come up with yet another flag that marks an element as erased, or something like that.

Inside of the type struct what is is the purpose of id?

In your example, the address of id acts as the key.

How is get being invoked here seemingly in a context outside of both info and type?

Via argument-dependent lookup.

What exactly is friend injection enabling here?

In this example, nothing useful.

In general it's used to smuggle information at compile-time, out of places that you normally can't. It's the basis of many advanced techniques.

For example, you can get the type of the current class in a macro, at class scope. If you play with it a bit, you'll notice that it's impossible to do without friend injection, as decltype(this) only works inside of member functions. So we use it in a member function, store the type using friend injection, and then load it back outside of the function.

Another fun trick is reconstructing the inheritance hierarchy of a class. There, friend injection is used to detect failed overload resolution attempts (!!), which can be used to find the base classes if they're marked in a certain way.

发布评论

评论列表(0)

  1. 暂无评论