Khám phá C++17: Khai báo using với độ dài biến đổi

Khai báo using

Đầu tiên, hãy xem cách sử dụng khai báo using trong lớp:

Mã 1

#include <iostream>

using namespace std;

struct Caculator {
    void tinh(int) {cout << "Caculator::tinh(int)" << endl;}
};

struct AdvancedCalculator : Caculator {
};

int main()
{
    AdvancedCalculator calc;
    calc.tinh(1); // Caculator::tinh(int)
}
  • Lớp AdvancedCalculator kế thừa hàm thành viên tinh từ lớp Caculator, do đó đối tượng calc của lớp AdvancedCalculator gọi hàm tinh sẽ xuất ra Caculator::tinh

Mã 2

#include <iostream>

using namespace std;

struct Caculator {
    void tinh(int) {cout << "Caculator::tinh(int)" << endl;}
};

struct AdvancedCalculator : Caculator {
    void tinh(double) {cout << "AdvancedCalculator::tinh(double)" << endl;}
};

int main()
{
    AdvancedCalculator calc;
    calc.tinh(1); // AdvancedCalculator::tinh(double)
}
  • Lớp AdvancedCalculator kế thừa hàm thành viên tinh từ lớp Caculator, đồng thời lớp AdvancedCalculator cũng định nghĩa hàm thành viên tinh
  • Hàm thành viên tinh của lớp AdvancedCalculator che khuất hàm thành viên cùng tên trong lớp cơ sở, do đó đối tượng calc của lớp AdvancedCalculator gọi hàm tinh sẽ xuất ra AdvancedCalculator::tinh

Mã 3

#include <iostream>

using namespace std;

struct Caculator {
    void tinh(int) {cout << "Caculator::tinh(int)" << endl;}
};

struct AdvancedCalculator : Caculator {
    using Caculator::tinh;
    void tinh(double) {cout << "AdvancedCalculator::tinh(double)" << endl;}
};

int main()
{
    AdvancedCalculator calc;
    calc.tinh(1); // Caculator::tinh(int)
    calc.tinh(2.5); // AdvancedCalculator::tinh(double)
}
  • Lớp AdvancedCalculator kế thừa hàm thành viên tinh từ lớp Caculator, đồng thời lớp AdvancedCalculator cũng định nghĩa hàm thành viên tinh
  • Lớp AdvancedCalculator sử dụng khai báo using để đưa hàm thành viên tinh của lớp cơ sở Caculator vào phạm vi của chính nó. Hàm thành viên tinh của lớp AdvancedCalculator và hàm thành viên cùng tên trong lớp cơ sở Caculator tạo thành quan hệ nạp chồng.
  • Khi tham số là kiểu nguyên, theo cơ chế nạp chồng, đối tượng calc của lớp AdvancedCalculator gọi hàm tinh sẽ xuất ra Caculator::tinh
  • Khi tham số là kiểu thực, theo cơ chế nạp chồng, đối tượng calc của lớp AdvancedCalculator gọi hàm tinh sẽ xuất ra AdvancedCalculator::tinh

Mã 4

#include <iostream>

using namespace std;

struct Caculator {
    void tinh(int) {cout << "Caculator::tinh(int)" << endl;}
};

struct ScientificCalculator {
    void tinh(double) {cout << "ScientificCalculator::tinh(double)" << endl;}
};

struct HybridCalculator : Caculator, ScientificCalculator {
};

int main()
{
    HybridCalculator calc;
    calc.tinh(1); // lỗi biên dịch
    calc.tinh(2.5); // lỗi biên dịch
}
  • Lớp HybridCalculator cùng kế thừa hàm thành viên tinh từ lớp Caculator và lớp ScientificCalculator, tạo ra quan hệ xung đột.
  • Bộ biên dịch không thể xác định hàm thành viên tinh mà đối tượng calc của lớp HybridCalculator gọi đến, do đó gây lỗi biên dịch.

Mã 5

#include <iostream>

using namespace std;

struct Caculator {
    void tinh(int) {cout << "Caculator::tinh(int)" << endl;}
};

struct ScientificCalculator {
    void tinh(double) {cout << "ScientificCalculator::tinh(double)" << endl;}
};

struct HybridCalculator : Caculator, ScientificCalculator {
    using Caculator::tinh;
    using ScientificCalculator::tinh;
};

int main()
{
    HybridCalculator calc;
    calc.tinh(1); // Caculator::tinh(int)
    calc.tinh(2.5); // ScientificCalculator::tinh(double)
}
  • Lớp HybridCalculator cùng kế thừa hàm thành viên tinh từ lớp Caculator và lớp ScientificCalculator.
  • Lớp HybridCalculator sử dụng khai báo using để đưa hàm thành viên tinh từ cả lớp cơ sở Caculator và ScientificCalculator vào phạm vi của chính nó. Các hàm thành viên cùng tên từ hai lớp cơ sở tạo thành quan hệ nạp chồng trong lớp HybridCalculator.
  • Khi tham số là kiểu nguyên, theo cơ chế nạp chồng, đối tượng calc của lớp HybridCalculator gọi hàm tinh sẽ xuất ra Caculator::tinh
  • Khi tham số là kiểu thực, theo cơ chế nạp chồng, đối tượng calc của lớp HybridCalculator gọi hàm tinh sẽ xuất ra ScientificCalculator::tinh

Khai báo using trong C++17

Trong C++17, nhiều khai báo using có thể được kết nối bằng dấu phẩy để tạo thành một khai báo using duy nhất.

Mã 6

#include <iostream>

using namespace std;

struct Caculator {
    void tinh(int) {cout << "Caculator::tinh(int)" << endl;}
};

struct ScientificCalculator {
    void tinh(double) {cout << "ScientificCalculator::tinh(double)" << endl;}
};

struct HybridCalculator : Caculator, ScientificCalculator {
    using Caculator::tinh, ScientificCalculator::tinh; // C++17
};

int main()
{
    HybridCalculator calc;
    calc.tinh(1); // Caculator::tinh(int)
    calc.tinh(2.5); // ScientificCalculator::tinh(double)
}

Khai báo using với độ dài biến đổi trong C++17

Bằng cách sử dụng khai báo using có dấu ..., chúng ta có thể chuyển đổi các khai báo using trong tham số mẫu kiểu độ dài biến đổi thành một khai báo using gồm nhiều phần được kết nối bằng dấu phẩy.

#include <iostream>
#include <string>

using namespace std;

template<class... Ts> struct multi_function : Ts... { using Ts::operator()...; };
template<class... Ts> multi_function(Ts...) -> multi_function<Ts...>;

int main()
{
    multi_function func{
        [](int){cout << "so nguyen" << endl;},
        [](double){cout << "so thuc" << endl;},
        [](string){cout << "chuoi" << endl;},
    };
    func(1); // so nguyen
    func(1.); // so thuc
    func("1"); // chuoi
}
  • template<class... Ts> struct multi_function : Ts... { using Ts::operator()...; }; Đây là khai báo của một mẫu lớp.
  • template<class... Ts>: Tham số mẫu của lớp multi_function là một bộ tham số có độ dài biến đổi Ts. Giả sử Ts chứa T1, T2, ... , TN, thì câu lệnh này có thể được mở rộng thành: template<class T1, class T2, ..., class TN>
  • struct multi_function : Ts...: Lớp cơ sở của lớp multi_function là tất cả các kiểu tham số trong bộ tham số Ts. Giả sử Ts chứa T1, T2, ... , TN, thì câu lệnh này có thể được mở rộng thành: struct multi_function : T1, T2, ..., TN
  • using Ts::operator()...;: Đây là khai báo using có độ dài biến đổi. Giả sử Ts chứa T1, T2, ... , TN, thì câu lệnh này có thể được mở rộng thành: using T1::operator(), T2::operator(), ..., TN::operator(); Điều này có nghĩa là tất cả toán tử gọi hàm của các lớp cơ sở (tức là các kiểu tham số trong bộ tham số Ts) đều được lớp multi_function đưa vào phạm vi của chính nó.
  • template<class... Ts> multi_function(Ts...) -> multi_function<Ts...>; Đây là hướng dẫn tự động suy luận, giúp bộ biên dịch suy luận kiểu tham số của lớp multi_function dựa trên kiểu của các tham số hàm tạo. Hướng dẫn tự động suy luận này cho bộ biên dịch biết rằng nếu các tham số hàm tạo của multi_function có kiểu thuộc tập hợp Ts, thì kiểu tham số của lớp multi_function chính là tất cả các kiểu trong Ts. Điều này có nghĩa là nếu biểu thức a1, a2, ..., có kiểu lần lượt là T1, T2, ..., TN, thì biểu thức hàm tạo multi_function {a1, a2, ..., an} sẽ có kiểu là multi_function<T1, T2, ..., TN>. * multi_function func{ [](int){cout << "so nguyen" << endl;}, [](double){cout << "so thuc" << endl;}, [](string){cout << "chuoi" << endl;}, }; Đối tượng func của lớp multi_function được tạo ra với 3 tham số lambda, có thể coi là 3 đối tượng hàm, mỗi đối tượng chứa một toán tử operator(). Theo định nghĩa của lớp multi_function, đối tượng func sẽ kế thừa toán tử operator() của cả 3 lambda (đối tượng hàm), tức là 3 toán tử operator() này tạo thành quan hệ nạp chồng trong đối tượng func. Theo hướng dẫn tự động suy luận của lớp multi_function, kiểu của đối tượng func là multi_function<T1, T2, T3>, trong đó T1, T2, T3 là kiểu của 3 tham số lambda.
  • Bằng cách sử dụng các tính năng mới của C++17 về khai báo using có độ dài biến đổi và hướng dẫn tự động suy luận, đối tượng của lớp multi_function có thể một cách ngắn gọn và khéo léo hợp nhất nhiều lambda thành một đối tượng hàm lớn có nhiều toán tử operator() nạp chồng nhau.
  • Lớp mẫu multi_function rất hữu ích và cơ chế thực hiện của nó tinh tế, xứng đáng được đưa vào thư viện chuẩn sớm hơn.

Thẻ: C++17 using-declaration templates variadic-templates operator-overloading

Đăng vào ngày 10 tháng 7 lúc 23:40