Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

C++  Module 7 – Classes, objects and RAII

Deducing this: explicit object parameters in C++23

Write C++23 member functions that take their object as an explicit this parameter: one function for every kind of object, recursive lambdas and less CRTP.

  • Intermediate
  • 20 minutes
  • Examples run with Clang 21.0.0, GCC 16.2.0 and Python 3.14.8
  • By MySmartCoPilot
Free: 20 minutes

What you will learn

  • Write member functions with an explicit object parameter
  • Replace duplicated const, non-const and rvalue overloads with one function template
  • Write recursive lambdas without std::function
  • Compare an explicit object parameter with the curiously recurring template pattern

Before you start

What you’ll learn · Before you start

What you will learn

  • Write member functions with an explicit object parameter
  • Replace duplicated const, non-const and rvalue overloads with one function template
  • Write recursive lambdas without std::function
  • Compare an explicit object parameter with the curiously recurring template pattern

Before you start

On this page

Deducing this is the informal name of a C++23 feature: a member function may declare the object it is called on as an explicit first parameter, marked with the keyword this, as in int get(this const Counter& self). Inside the function you write self.n where you used to write n or this->n, and callers still write c.get(). Because the object is now an ordinary parameter, its type can be deduced like any template parameter. One function template, auto&& value(this Self&& self), then serves const and non-const objects, lvalues and rvalues, which used to take four overloads. The same trick lets a lambda call itself, and lets a base class see the derived class it is called on without the curiously recurring template pattern (CRTP).

This lesson writes explicit object parameters by hand, collapses four overloads into one, writes a recursive depth-first search as a lambda and replaces a CRTP base. GCC supports the feature from version 14 and Clang from version 18 (paper P0847R7); every example runs with Apple clang 21 and GCC 16.

The object becomes a named parameter

The explicit object parameter is the first parameter, written with this in front of it. Its name is up to you, and self is the usual choice. Its type says how the function receives the object, so the const that used to follow the parameter list moves into the parameter:

explicit_basics.cpp C++ · explicit_basics.cpp
// The object as an explicit first parameter, marked with this (C++23).
#include <print>

struct Counter {
    int n = 0;

    void bump(this Counter& self) { ++self.n; }                // the object is self, not an implicit this
    int get(this const Counter& self) { return self.n; }       // const goes on the parameter's type
    Counter doubled(this Counter self) {                       // by value: works on a copy
        self.n *= 2;
        return self;
    }
};

int main() {
    Counter c;
    c.bump();   // called exactly as before
    c.bump();
    std::println("c.get() = {}", c.get());

    Counter d = c.doubled();
    std::println("d.get() = {}, c.get() = {}", d.get(), c.get());

    // The address of such a member is an ordinary function pointer: the object is just its first argument.
    int (*read)(const Counter&) = &Counter::get;
    std::println("read(d) = {}", read(d));
    std::println("__cpp_explicit_this_parameter = {}", __cpp_explicit_this_parameter);
}
Called as before Python · basics_session.py
from session import run

# explicit_basics.cpp built and run with Apple clang, then with GCC 16.
run("""
clang++ -std=c++23 -Wall -Wextra explicit_basics.cpp -o explicit_basics && ./explicit_basics
g++-16 -std=c++23 -Wall -Wextra explicit_basics.cpp -o explicit_basics-gcc && ./explicit_basics-gcc
""")

Output

$ clang++ -std=c++23 -Wall -Wextra explicit_basics.cpp -o explicit_basics && ./explicit_basics
c.get() = 2
d.get() = 4, c.get() = 2
read(d) = 4
__cpp_explicit_this_parameter = 202110
$ g++-16 -std=c++23 -Wall -Wextra explicit_basics.cpp -o explicit_basics-gcc && ./explicit_basics-gcc
c.get() = 2
d.get() = 4, c.get() = 2
read(d) = 4
__cpp_explicit_this_parameter = 202110

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 basics_session.py

Three kinds of parameter appear in Counter:

  • this Counter& self receives the object by reference, like an ordinary member function, so bump() changes c.
  • this const Counter& self makes get() a read-only function, as a const member function is, and it can be called on a const Counter.
  • this Counter self receives a copy. doubled() changes and returns that copy, so d holds 4 while c keeps 2. Taking a small object by value suits functions that compute a modified version of it.

The calls do not change: c.bump() passes c as self. The address of such a function, &Counter::get, is an ordinary pointer to a function that takes a const Counter&, called as read(d), because the object really is just the first argument. The last line prints the feature-test macro, 202110, which code can check with #if before using the feature.

Inside such a function there is no implicit this, so a member name on its own means nothing:

no_implicit_this.cpp C++ · no_implicit_this.cpp
// In a function with an explicit object parameter, member names need self: there is no implicit this.
struct Counter {
    int n = 0;
    int get(this const Counter& self) { return n; }   // error: n is not self.n here
};

int main() { return Counter{}.get(); }
n needs self Python · no_this_session.py
from session import run

# What each compiler says about no_implicit_this.cpp.
run("""
clang++ -std=c++23 -fsyntax-only no_implicit_this.cpp
g++-16 -std=c++23 -fsyntax-only no_implicit_this.cpp
""")

Output

$ clang++ -std=c++23 -fsyntax-only no_implicit_this.cpp
no_implicit_this.cpp:4:48: error: invalid use of member 'n' in explicit object member function
    4 |     int get(this const Counter& self) { return n; }   // error: n is not self.n here
      |                                                ^
      |                                                self.
1 error generated.
[exit status 1]
$ g++-16 -std=c++23 -fsyntax-only no_implicit_this.cpp
no_implicit_this.cpp: In explicit object member function 'int Counter::get(this const Counter&)':
no_implicit_this.cpp:4:48: error: invalid use of non-static data member 'Counter::n'
    4 |     int get(this const Counter& self) { return n; }   // error: n is not self.n here
      |                                                ^
no_implicit_this.cpp:3:9: note: declared here
    3 |     int n = 0;
      |         ^
[exit status 1]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 no_this_session.py

Clang even offers the fix, self.. The standard adds a few more rules: a function with an explicit object parameter cannot be static or virtual, and it cannot have const or a reference qualifier such as && after its parameter list, since the parameter’s type already says all of that.

One function instead of four overloads

A wrapper that hands out its contents needs a different result for each kind of object. A non-const box gives a reference that callers can write through, a const box a reference to const, and a box that is about to disappear, an rvalue, should let its contents be moved out rather than copied. Before C++23 that took four overloads with the same body. With an explicit object parameter it takes one template:

one_value.cpp C++ · one_value.cpp
// One value() for const and non-const, lvalue and rvalue objects, instead of four overloads.
#include <print>
#include <string>
#include <type_traits>
#include <utility>

template <class T>
class Box {
public:
    explicit Box(T v) : value_(std::move(v)) {}

    // Before C++23 this took four overloads:
    //   T& value() &;   const T& value() const&;   T&& value() &&;   const T&& value() const&&;
    // Self is deduced from the object, and std::forward_like gives value_ the same constness and value category.
    template <class Self>
    auto&& value(this Self&& self) {
        return std::forward_like<Self>(self.value_);
    }

private:
    T value_;
};

// Names the kind of reference an expression of type R is.
template <class R>
const char* kind() {
    using V = std::remove_reference_t<R>;
    if constexpr (std::is_lvalue_reference_v<R>) return std::is_const_v<V> ? "const std::string&" : "std::string&";
    else return std::is_const_v<V> ? "const std::string&&" : "std::string&&";
}

int main() {
    Box<std::string> box(std::string("chai"));
    const Box<std::string>& view = box;

    std::println("box.value()                  {}", kind<decltype(box.value())>());
    std::println("view.value()                 {}", kind<decltype(view.value())>());
    std::println("std::move(box).value()       {}", kind<decltype(std::move(box).value())>());
    std::println("std::move(view).value()      {}", kind<decltype(std::move(view).value())>());

    box.value() += " and samosa";                      // a reference into the box
    std::println("in the box: {}", view.value());

    std::string taken = std::move(box).value();        // moved out of a box that is going away
    std::println("taken: {}", taken);
}
Four kinds of object, four results, one function Python · value_session.py
from session import run

# one_value.cpp built and run with both compilers.
run("""
clang++ -std=c++23 -Wall -Wextra one_value.cpp -o one_value && ./one_value
g++-16 -std=c++23 -Wall -Wextra one_value.cpp -o one_value-gcc && ./one_value-gcc
""")

Output

$ clang++ -std=c++23 -Wall -Wextra one_value.cpp -o one_value && ./one_value
box.value()                  std::string&
view.value()                 const std::string&
std::move(box).value()       std::string&&
std::move(view).value()      const std::string&&
in the box: chai and samosa
taken: chai and samosa
$ g++-16 -std=c++23 -Wall -Wextra one_value.cpp -o one_value-gcc && ./one_value-gcc
box.value()                  std::string&
view.value()                 const std::string&
std::move(box).value()       std::string&&
std::move(view).value()      const std::string&&
in the box: chai and samosa
taken: chai and samosa

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 value_session.py

this Self&& self is a forwarding reference, so Self records what kind of object the call was made on. std::forward_like<Self> (C++23, paper P2445R1) then returns the member with the same constness and value category. For a Box<std::string>:

  • box.value() deduces Self as Box& and returns std::string&.
  • view.value(), where view is a const Box&, deduces const Box& and returns const std::string&.
  • std::move(box).value() deduces Box and returns std::string&&.
  • std::move(view).value() deduces const Box and returns const std::string&&.

That is why box.value() += " and samosa" changes the string inside the box, and why std::string taken = std::move(box).value(); moves the string out instead of copying it. Returning self.value_ without std::forward_like would give an lvalue reference in every case, so the rvalue call would copy the string and the box would never lend its contents to a move. The two cases that a single const member function cannot express, writing through a non-const object and moving out of an rvalue, are exactly what the duplicated overloads used to exist for.

Recursive lambdas

A lambda has no name inside its own body, so before C++23 it could not call itself directly. With an explicit object parameter, the lambda receives itself as self and recurses through it:

recursive_lambda.cpp C++ · recursive_lambda.cpp
// Lambdas that call themselves through their explicit object parameter.
#include <functional>
#include <print>
#include <vector>

int main() {
    auto factorial = [](this auto self, long long n) -> long long { return n <= 1 ? 1 : n * self(n - 1); };
    std::println("20! = {}", factorial(20));

    // Depth-first search: the order in which the nodes of a small graph are first reached from node 0.
    std::vector<std::vector<int>> next{{1, 2}, {3}, {3, 4}, {}, {}};
    std::vector<bool> seen(next.size(), false);
    std::vector<int> order;
    auto dfs = [&](this const auto& self, int u) -> void {
        seen[u] = true;
        order.push_back(u);
        for (int v : next[u])
            if (!seen[v]) self(v);
    };
    dfs(0);
    std::println("visit order {}", order);

    // The older way: a std::function that the lambda captures, so that it can call itself.
    std::function<void(int)> old_dfs = [&](int u) {
        seen[u] = true;
        for (int v : next[u])
            if (!seen[v]) old_dfs(v);
    };
    std::println("sizeof(dfs) = {}, sizeof(std::function<void(int)>) = {}", sizeof(dfs), sizeof(old_dfs));
}
A factorial, a search, and two sizes Python · lambda_session.py
from session import run

# recursive_lambda.cpp built and run with both compilers.
run("""
clang++ -std=c++23 -Wall -Wextra recursive_lambda.cpp -o recursive_lambda && ./recursive_lambda
g++-16 -std=c++23 -Wall -Wextra recursive_lambda.cpp -o recursive_lambda-gcc && ./recursive_lambda-gcc
""")

Output

$ clang++ -std=c++23 -Wall -Wextra recursive_lambda.cpp -o recursive_lambda && ./recursive_lambda
20! = 2432902008176640000
visit order [0, 1, 3, 2, 4]
sizeof(dfs) = 24, sizeof(std::function<void(int)>) = 32
$ g++-16 -std=c++23 -Wall -Wextra recursive_lambda.cpp -o recursive_lambda-gcc && ./recursive_lambda-gcc
20! = 2432902008176640000
visit order [0, 1, 3, 2, 4]
sizeof(dfs) = 24, sizeof(std::function<void(int)>) = 32

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 lambda_session.py

factorial takes this auto self by value, a copy of a lambda that captures nothing, which costs nothing. dfs captures three variables by reference, and each call self(v) goes one level deeper into the graph, so it reaches the nodes in the order 0, 1, 3, 2, 4. It takes this const auto& self, which never copies the lambda; that matters once a lambda captures something large by value, because a by-value self copies all its captures at every level of recursion.

The older workaround stored the lambda in a std::function that the lambda captured by reference. It works, but std::function erases the lambda’s type, calls it through an indirect pointer that the optimiser can rarely inline, and may allocate memory for larger lambdas. The last line measures the two objects with both libraries: the lambda is 24 bytes, its three references, and the std::function 32.

Replacing the curiously recurring template pattern

CRTP lets a base class call functions of the class derived from it: the base is a template, the derived class names itself as the template argument, and the base casts *this to that type. An explicit object parameter gives the same result with an ordinary base class, because self is deduced as the type of the object the call is made on:

static_polymorphism.cpp C++ · static_polymorphism.cpp
// The same mixin twice: with the curiously recurring template pattern, and with an explicit object parameter.
#include <print>
#include <string>

// CRTP: the base is a template that is told the derived type, and casts *this to it.
template <class Derived>
struct DescribableCrtp {
    void describe() const {
        const auto& self = static_cast<const Derived&>(*this);
        std::println("{} with area {}", self.name(), self.area());
    }
};

// C++23: an ordinary base class; self is deduced as the type of the object it is called on.
struct Describable {
    void describe(this const auto& self) { std::println("{} with area {}", self.name(), self.area()); }
};

struct Square : DescribableCrtp<Square> {
    double side = 3;
    std::string name() const { return "square"; }
    double area() const { return side * side; }
};

struct Rectangle : Describable {
    double w = 4, h = 2.5;
    std::string name() const { return "rectangle"; }
    double area() const { return w * h; }
};

int main() {
    Square{}.describe();
    Rectangle{}.describe();
}
base_reference.cpp C++ · base_reference.cpp
// Through a reference to the base class, self is deduced as the base, which has no name() or area().
#include <print>
#include <string>

struct Describable {
    void describe(this const auto& self) { std::println("{} with area {}", self.name(), self.area()); }
};

struct Rectangle : Describable {
    double w = 4, h = 2.5;
    std::string name() const { return "rectangle"; }
    double area() const { return w * h; }
};

void show(const Describable& d) { d.describe(); }   // error: Describable has no member name()

int main() { show(Rectangle{}); }
Both mixins, and the base-reference error Python · crtp_session.py
from session import run

# static_polymorphism.cpp with both compilers, then the errors each one reports in base_reference.cpp.
run("""
clang++ -std=c++23 -Wall -Wextra static_polymorphism.cpp -o static_polymorphism && ./static_polymorphism
g++-16 -std=c++23 -Wall -Wextra static_polymorphism.cpp -o static_polymorphism-gcc && ./static_polymorphism-gcc
clang++ -std=c++23 -fsyntax-only base_reference.cpp 2>&1 | grep -E "^base_reference.cpp:[0-9:]+ error"
g++-16 -std=c++23 -fsyntax-only base_reference.cpp 2>&1 | grep -E "^base_reference.cpp:[0-9:]+ error"
""")

Output

$ clang++ -std=c++23 -Wall -Wextra static_polymorphism.cpp -o static_polymorphism && ./static_polymorphism
square with area 9
rectangle with area 10
$ g++-16 -std=c++23 -Wall -Wextra static_polymorphism.cpp -o static_polymorphism-gcc && ./static_polymorphism-gcc
square with area 9
rectangle with area 10
$ clang++ -std=c++23 -fsyntax-only base_reference.cpp 2>&1 | grep -E "^base_reference.cpp:[0-9:]+ error"
base_reference.cpp:6:81: error: no member named 'name' in 'Describable'
$ g++-16 -std=c++23 -fsyntax-only base_reference.cpp 2>&1 | grep -E "^base_reference.cpp:[0-9:]+ error"
base_reference.cpp:6:81: error: 'const struct Describable' has no member named 'name'
base_reference.cpp:6:94: error: 'const struct Describable' has no member named 'area'

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 crtp_session.py

Square{}.describe() goes through the CRTP base, and Rectangle{}.describe() through the plain base class with this const auto& self; both print the derived class’s name and area. The C++23 version needs no template argument, no cast and no chance of naming the wrong derived class, a classic CRTP slip that compiles and then misbehaves.

Both techniques work with the type the compiler knows, not with the type of the object at run time. base_reference.cpp calls describe() through a const Describable&, so self is deduced as Describable, which has no name() or area(), and both compilers reject it. When code must handle different shapes through one base class reference, use virtual functions, which the next module covers. Explicit object parameters replace CRTP where CRTP was only a way to reach the derived class while compiling.

Your turn: one subscript operator

Names in names.h defines its subscript operator four times, for non-const and const objects, each as an lvalue and as an rvalue. Replace the four definitions with one member function template that has an explicit object parameter, keeping every result type exactly as it is:

The starter code and its sample tests

C header · names.h

// Starter code for "Your turn": replace the four subscript overloads with one explicit-object member.
#pragma once
#include <cstddef>
#include <string>
#include <utility>
#include <vector>

class Names {
public:
    explicit Names(std::vector<std::string> v) : items_(std::move(v)) {}

    // One overload for each kind of object: non-const and const, lvalue and rvalue.
    std::string& operator[](std::size_t i) & { return items_[i]; }
    const std::string& operator[](std::size_t i) const& { return items_[i]; }
    std::string&& operator[](std::size_t i) && { return std::move(items_[i]); }
    const std::string&& operator[](std::size_t i) const&& { return std::move(items_[i]); }

    std::size_t size() const { return items_.size(); }

private:
    std::vector<std::string> items_;
};

C++ · names_tests.cpp

// Sample tests for names.h. Build: clang++ -std=c++23 -Wall -Wextra names_tests.cpp -o names_tests
#include <fstream>
#include <print>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include "names.h"

int checks = 0;
int failures = 0;

void check(bool ok, const char* what) {
    ++checks;
    if (!ok) {
        ++failures;
        std::println("FAIL {}", what);
    }
}

// How many times names.h spells out the subscript operator's name.
int definitions_in_header() {
    std::ifstream file("names.h");
    std::stringstream text;
    text << file.rdbuf();
    std::string s = text.str();
    int count = 0;
    for (std::size_t at = s.find("operator[]"); at != std::string::npos; at = s.find("operator[]", at + 1)) ++count;
    return count;
}

int main() {
    Names n({"Asha", "Dev", "Meera"});
    const Names& c = n;

    check(std::is_same_v<decltype(n[0]), std::string&>, "a non-const Names gives std::string&");
    check(std::is_same_v<decltype(c[0]), const std::string&>, "a const Names gives const std::string&");
    check(std::is_same_v<decltype(std::move(n)[0]), std::string&&>, "an rvalue Names gives std::string&&");
    check(std::is_same_v<decltype(std::move(c)[0]), const std::string&&>, "a const rvalue Names gives const std::string&&");

    n[0] = "Aarav";
    check(c[0] == "Aarav", "writing through n[0] changes the element");
    std::string moved = std::move(n)[1];
    check(moved == "Dev", "std::move(n)[1] hands over the element");
    check(n.size() == 3, "moving one element out keeps the size");

    int defs = definitions_in_header();
    check(defs == 1, "names.h defines the subscript operator once");
    if (defs != 1) std::println("  names.h spells operator[] {} times", defs);

    std::println("{} of {} checks passed", checks - failures, checks);
    return failures == 0 ? 0 : 1;
}

The tests check the four result types with std::is_same_v, check that writing and moving through the operator still work, and read names.h itself to count how often it spells out the operator’s name. The starter code passes every check but that last one:

The sample tests against the starter code Python · names_session.py
from session import run

# The sample tests run against the starter code, before it is rewritten.
run("""
clang++ -std=c++23 -Wall -Wextra names_tests.cpp -o names_tests && ./names_tests
g++-16 -std=c++23 -Wall -Wextra names_tests.cpp -o names_tests-gcc && ./names_tests-gcc
""")

Output

$ clang++ -std=c++23 -Wall -Wextra names_tests.cpp -o names_tests && ./names_tests
FAIL names.h defines the subscript operator once
  names.h spells operator[] 4 times
7 of 8 checks passed
[exit status 1]
$ g++-16 -std=c++23 -Wall -Wextra names_tests.cpp -o names_tests-gcc && ./names_tests-gcc
FAIL names.h defines the subscript operator once
  names.h spells operator[] 4 times
7 of 8 checks passed
[exit status 1]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 names_session.py

This is a refactoring: the behaviour checks pass before you start, and they must still pass when you finish, now with 8 of 8 checks passed. Mind the hint in the previous section: return self.items_[i]; on its own compiles, yet it returns an lvalue reference even for an rvalue Names, which fails the two rvalue type checks. Keep the comments of names.h free of the operator’s name, since the test counts every occurrence.

Interview questions

Fresher · What does this auto&& self deduce for a non-const lvalue, a const lvalue and an rvalue object?

this auto&& self is a forwarding reference, so it deduces from how the function is called. On a non-const lvalue obj, self is T&; on a const lvalue it is const T&; on an rvalue such as std::move(obj) or a temporary it is T&&, where T is the object’s type. The template parameter behind auto, written out as Self, keeps that information, so std::forward<Self>(self) passes the object on as it came, and std::forward_like<Self>(self.member) gives a member the same constness and value category. That is what lets one function replace the &, const&, && and const&& overloads. A good answer also notes that T can be a class derived from the one that declares the function, because the type is deduced from the object the function is called on.

Key takeaways

  • A C++23 member function can declare its object as the first parameter, this Type self, and must then use self.member; there is no implicit this inside it.
  • The parameter’s type replaces const and reference qualifiers: this const T& reads, this T& writes, and this T works on a copy.
  • template <class Self> auto&& f(this Self&& self) with std::forward_like<Self> replaces four overloads.
  • A lambda with this auto self, or this const auto& self when it captures much, can call itself without std::function.
  • Deduction uses the static type at each call, so explicit object parameters replace CRTP but not virtual functions.

Check yourself

6 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 6 What does this program print?

    Read the code, then choose one answer.

    struct Counter {
        int n = 0;
        Counter doubled(this Counter self) { self.n *= 2; return self; }
    };
    int main() {
        Counter c{3};
        Counter d = c.doubled();
        std::println("{} {}", c.n, d.n);
    }
    Show the answer to question 1

    Answer: 3 6

    this Counter self receives a copy of c. doubled() changes and returns that copy, so d.n is 6 while c.n stays 3.

  2. Question 2 of 6 Inside int get(this const Counter& self), what does writing return n; for the member n do?

    Choose one answer.

    Show the answer to question 2

    Answer: It does not compile; the member must be written self.n

    A function with an explicit object parameter has no implicit this, so a member name on its own cannot refer to the object. Clang even suggests self. as the fix.

  3. Question 3 of 6 template <class Self> auto&& value(this Self&& self) is called as std::move(box).value(), where box is a non-const Box. What is Self?

    Choose one answer.

    Show the answer to question 3

    Answer: Box

    For an rvalue argument, a forwarding reference deduces its template parameter as the plain type, Box, and the parameter itself is then Box&&. For a non-const lvalue Self would be Box&, and for a const lvalue const Box&.

  4. Question 4 of 6 Why does value() return std::forward_like<Self>(self.value_) instead of self.value_?

    Choose one answer.

    Show the answer to question 4

    Answer: Because self.value_ is always an lvalue, so an rvalue box could never let its contents be moved out

    self is a named reference, so self.value_ is an lvalue in every case, const for a const box. forward_like gives the member the value category of the object as well, so std::move(box).value() yields std::string&&.

  5. Question 5 of 6 Which of these may a member function with an explicit object parameter not be or have?

    Choose every answer that is right.

    Show the answer to question 5

    Answer:

    • A const qualifier after the parameter list
    • static
    • virtual

    The standard forbids static, virtual, cv-qualifiers and ref-qualifiers on such a function, because the explicit parameter's type already describes the object. It may be a template, which is its main use, and it is called as before.

  6. Question 6 of 6 struct Describable { void describe(this const auto& self) { self.name(); } }; What happens when describe() is called through a const Describable& that refers to a Rectangle?

    Choose one answer.

    Show the answer to question 6

    Answer: It does not compile, because self is deduced as Describable, which has no name()

    self is deduced from the type the compiler sees at the call, here the base class. Choosing a function by the object's type at run time needs a virtual function.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.