Overloaded Functions Returning Const & Non-Const Pair<>

Feb 19, 2023 at 2:55am
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 Feb 19, 2023 at 3:26am
Feb 19, 2023 at 4:49am
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 Feb 20, 2023 at 6:01am
Feb 19, 2023 at 10:43am
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 Feb 19, 2023 at 10:46am
Feb 20, 2023 at 5:52am
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.