Overloaded Functions Returning Const & Non-Const Pair<>

MSVS 2022 17.4.5 (v143), /stdc++latest

I'm experimenting with templates and const-ness. How do I ensure the correct function call to return a const?

This is slightly abstracted/reduced code from a larger file (I know, this might not be fair to those trying to run the code, but I was looking for a quick answer to something obvious I'm missing) for clarity and brevity. The "print" function is a wrapper around std::formatter and std::vformat to output the contents of myObject objects using the std::format library (hence the import <format>;). It works just fine.

I've attempted to use std::as_const and const_cast<> without success.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import <format>;
import <iostream>;
import <string>;
import <tuple>;
import <utility>;

using std::cout;
using std::string;

template <typename T>
struct myObject {
  T data;
  string status;
  const std::pair<T, string> info() const noexcept;
  std::pair<T, string> info() noexcept;
};

// Make a std::pair from the data members of the myObject class
template <typename T>
const std::pair<T, string> myObject<T>::info() const noexcept
{
  return std::pair<T, string>(std::make_pair(this->data, this->status + " const"));
}

template <typename T>
std::pair<T, string> myObject<T>::info() noexcept
{
  return std::pair<T, string>(std::make_pair(this->data, this->status + " non-const"));
}

int main ()
{
  myObject testObject1{ 42, "Int" };
  myObject testObject2{ 3.1, "Float" };
  myObject testObject3{ 300.12, "Double" };
  const myObject testObject4{ "String", "String" };
	
  auto myTObj1{ testObject4.info() };
  auto myTObj2{ testObject2.info() };
  const auto myTObj3{ testObject3.info() };

  print("From pair Object 1: {:>7}, {:<16} {}\n", myTObj1.first, myTObj1.second, "- Should be const");
  print("From pair Object 2: {:>7}, {:<16} {}\n", myTObj2.first, myTObj2.second, "- Should be non-const");
  print("From pair Object 3: {:>7}, {:<16} {}\n", myTObj3.first, myTObj3.second, "- Why not const??");
}


The output is
1
2
3
From pair Object 1:  String, String const     - Should be const
From pair Object 2:     3.1, Float non-const  - Should be non-const
From pair Object 3:  300.12, Double non-const - Why not const??


Why isn't the const version of .info() being called for the const myTObj3 object?
Last edited on
Why isn't the const version of .info() being called for the const myTObj3 object?

The declaration of myTObj3 is here:
const auto myTObj3{ testObject3.info() };

The type of testObject3 is myObject<double>. Because this is not const-qualified the non-const overload is used.

Fix it by ensuring the left of the dot has a const-qualified type:
auto myTObj3{ std::as_const(testObject3).info() };
Last edited on
Why isn't the const version of .info() being called for the const myTObj3 object?


Because L35 testObject3 isn't defined as const. Consider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <string>
#include <utility>
#include <format>

using std::cout;
using std::string;

template <typename T>
struct myObject {
	T data;
	string status;

	const std::pair<T, string> info() const noexcept;
	std::pair<T, string> info() noexcept;
};

// Make a std::pair from the data members of the myObject class
template <typename T>
const std::pair<T, string> myObject<T>::info() const noexcept {
	return std::pair<T, string>(std::make_pair(this->data, this->status + " const"));
}

template <typename T>
std::pair<T, string> myObject<T>::info() noexcept {
	return std::pair<T, string>(std::make_pair(this->data, this->status + " non-const"));
}

int main() {
	myObject testObject1 { 42, "Int" };
	myObject testObject2 { 3.1, "Float" };
	const myObject testObject3 { 300.12, "Double" };
	const myObject testObject4 { "String", "String" };

	auto myTObj1 { testObject4.info() };
	auto myTObj2 { testObject2.info() };
	const auto myTObj3 { testObject3.info() };

	cout << std::format("From pair Object 1: {:>7}, {:<16} {}\n", myTObj1.first, myTObj1.second, "- Should be const");
	cout << std::format("From pair Object 2: {:>7}, {:<16} {}\n", myTObj2.first, myTObj2.second, "- Should be non-const");
	cout << std::format("From pair Object 3: {:>7}, {:<16} {}\n", myTObj3.first, myTObj3.second, "- Is now const");
}


which displays:


From pair Object 1:  String, String const     - Should be const
From pair Object 2:     3.1, Float non-const  - Should be non-const
From pair Object 3:  300.12, Double const     - Is now const

Last edited on
Thanks for the advice, both of you.

mbozzi, that's it! My parentheses had encompassed the entire call to .info(), which explains why I was getting compile-time errors on:
const auto myTObj3 { std::as_const(testObject3.info()) };
Topic archived. No new replies allowed.